Nested pattern matching in Scala - scala

I'm new to Scala and I'm trying to understand how pattern matching works. So I wrote this basic code, which returned the expected result:
def test(choice: Int): String = choice match {
case x if x > 0 && x%2 == 0 => "positive even number"
case x if x > 0 && x%2 != 0 => "positive odd number"
case 0 => "null"
case x if x < 0 && x%2 == 0 => "negative even number"
case x if x < 0 && x%2 != 0 => "negative odd number"
}
Now I'm trying to do something a little more elaborate:
def test(choice: Int): String = choice match {
case x if x%2 == 0 => x match {
case y if y > 0 => "even and positive number"
case y if y < 0 => "even and negative number"
}
case 0 => "null"
case x if x%2 != 0 => x match {
case y if y > 0 => "odd and positive number"
case y if y < 0 => "odd and negative number"
}
}
But it failed. Here's the error message on the console:
scala> def test(choice: Int): String = choice match {
|
| case x if x%2 == 0 => x match {
| case y if y > 0 => "even and positive number"
| Display all 600 possibilities? (y or n)
[...]
| if y < 0 => "even and negative number"
<console>:5: error: '(' expected but identifier found.
if y < 0 => "even and negative number"
^
[...]
Can somebody tell me what I'm doing wrong and give me some details about what I misunderstand about Scala in general and the match method in particular.

It compiles for me. The order of cases doesn't matter for the success of compilation (however, the case 0 branch will never match, because case x if x%2==0 matches x=0. You may want to make the case 0 branch the first one)
I believe your problem is because of using tabs instead of spaces in the terminal.
If you use this code in a file in a project, it'll compile just as well. If you want it to work in the console, you can either:
convert the tabs to spaces before pasting the code
enter paste mode in the REPL using the :paste command, paste the code and exit paste mode with Ctrl-D (or whatever the REPL tells you the combination is - that's the one on Mac).

In your first code, you test for x>0 in the first 2 cases, then for 0 itself.
In the second code, you don't test for x>0, but x%2 == 0 which already matches for x = 0, and a second, outer match isn't considered.
If you put the explicit 0 match on top, it might work, but I didn't look for a second error and only matched for the first error I could find. :)

Related

Scala: simplify case with comparison

Let's say, I have the following code snippet:
val num = Future.successful(10)
num map {
case n if n > 0 => // do something
case _ // do something
}
My question is: can I simplify case n if n > 0 somehow?
I expected that I can write something like:
case _ > 0 => // do something
or with explicitly specified type (although we know that Future has inferred type [Int]):
case _: Int > 0 => // do something
Can this code be simplified somehow?
You can't simplify case n if n > 0 => ....
Every case clause in a pattern match needs to have a pattern and (optionally) a guard.
The syntax you are referring to (_ > 0) is only valid in lambdas, but there's no similar special syntax for case clauses.
If you want to simplify the guard, you can filter the Future a priori:
val num = Future.successful(10).filter(_ > 0).map { nat =>
}
Otherwise, you can keep the guard and use Future.collect:
val num = Future.successful(10).collect {
case n if n > 0 => // do something
}
One important thing to note is that if the partial function is not defined for the value which returned (i.e for your case -1) then the resulting future will be a Failure containing a NoSuchElementException.
Other than these options, you'll need the guard. I don't see any syntactically shorter way to express it.

Block statements in anonymous Scala function

I've created this simple anonymous function
var b = (x : Int) => if(x % 2 == 0) x + 1 else x
and it works great. After that I tried to add another statement after the if and before the x+1 statement.
var b = (x : Int) => if(x % 2 == 0) println(x) x + 1 else x
and a that point I received the following compiler error
Cannot resolve symbol x
Please can anyone let me know why this happen?
The reason this happens is that although Scala does not require the use of semi-colons most of the time (unlike Java), since the compiler is more equipped to infer where statements/expressions end, if you have 2 statements/expressions on 1 line then you need to separate them for the compiler.
Your first anonymous function works since if(x % 2 == 0) x + 1 else x is 1 expression.
The compiler is complaining with the second one though since if(x % 2 == 0) println(x) is considered 1 statement. Hence, the next statement starts and there is now no context for x.
Other posters have given you a solution to break the right-side of the function down into separate statements so I won't duplicate - just adding an explanation of why the compilation error occurs since you said you are learning the language. Google about the use of semi-colons in Scala to find out more.
Use this. You need the braces to indicate these are multiple lines:
var b = (x : Int) => if(x % 2 == 0) {
println(x)
x + 1
} else x
Or you can do this.. you need to put that semi colon to indicate to compiler they are separate statements:
var b = (x : Int) => if(x % 2 == 0) { println(x); x + 1 } else x

Do if else statements in Scala always require an else?

I have the following function which works fine:
def power(x: Double, n: Int) : Double = {
if (n > 0 && n % 2 == 0) power(x, n/2) * power(x, n/2)
else if (n > 0 && n % 2 == 1) x * power(x, n-1)
else if (n < 0) 1 / power(x, -n)
else 1
}
If I change it to be:
def power(x: Double, n: Int) : Double = {
if (n > 0 && n % 2 == 0) power(x, n/2) * power(x, n/2)
else if (n > 0 && n % 2 == 1) x * power(x, n-1)
else if (n < 0) 1 / power(x, -n)
else if (n==0 ) 1
}
I.e. change the final else statement to be an else if, then I get the following error trying to call the function:
> <console>:8: error: not found: value power
power(2, 1)
^
I'm guessing this is because there is a possible return type of Unit because the value of n could meet none of the conditions?
In Java "if-else" is a statement. In Scala it is an expression (think of Java's ternary ?:), meaning that it always produces some value. As mentioned by som-snytt in comments, in case of a missing else block the compiler supplies else (), which is of type Unit, which obviously conflicts with the expected type Double in your example.
Some valid examples of missing else are provided in Chris's answer.
No - In general, an if expression does not require an else clause.
Examples:
if (true) println("a")
// prints "a"
if (false) println("a") else if (true) println("b")
// prints "b"
As Nikita Volkov's answer says, though, it is necessary if you need the expression to have some type other than Unit.
Your guess is correct. The method must return a Double, yours would return Unit if non of the "if.." cases match. Actually, when you paste the second definition into the repl, you should get
<console>:11: error: type mismatch;
found : Unit
required: Double

A way to write this more concise? Need to check if a number is under 0

val total_breaks = //a random number
total_breaks match {
case i if(i < 0) => chartTemplate.setAttribute("totalBreaks", 0)
case _ => chartTemplate.setAttribute("totalBreaks", total_breaks)
}
I was thinking there was a function in Scala that could shorten this. I thought min did this but I guess not. I can't seem to find documentation on min, max, etc.
Something like total_breaks.min(0). Display 0 if under 0 if not display total_breaks.
Also is there a way do something like this
(4 + 5) match {
case 0 => println("test")
case _ => println(_) //i need to display the number passed into match? Is this not possible?
}
If I do case i => println(i) is that the same as case _ => ? Is that the fallback?
There are methods min and max defined in GenTraversableOnce, and thus available on sequences. You can use them as:
scala> List(1, -4, 0).min
resN: -4
There is also min and max defined in RichInt, that work like operators on anything that can be converted to RichInt, typically your vanilla integers:
scala> -4 min 0
resN: -4
So if you want something that returns your number, say x if x is greater than 0 and 0 otherwise, you can write:
scala> x max 0
That means you can rewrite your pattern-matching as:
chartTemplate.setAttribute("totalBreaks", total_breaks max 0)
For your second question, _ and i are both valid patterns that will match anything. The difference is that in the first case you do not bind what you have matched to a variable. Using println(_) is wrong, though; as such, it corresponds to an anonymous function that prints its first argument. So if you don't want to repeat the expression (4 + 5), you should indeed write your pattern and code as:
case i => println(i)

Match, case with logic?

def searchEquipmentCategory(category: String) = Action {
val equipment = Equipment.searchByCategory(category)
equipment.size match {
case 0 => NotFound(views.html.helpers.notfound("Equipment not found for category :" + category))
case (_ > 0) => Ok(views.html.equipment.index(equipment, capitalize(category)))
}
}
Is it possible put logic in a match case statement?
I've searched everywhere and can't find any documentation. I just want to have if the case is 0 do one thing if the number is over 0.
Using the _ default works fine in that situation, but what if I wanted to do 3 things?
if number == 0
if the number is between 1 and 10
if the number is between 11 and 20
Maybe I'm trying to do too much with case.
Thanks for the help.
case i if i > 0 => Ok( ... )
So to distinguish between 0, 1 to 10 and 11 to 20:
case 0 =>
case i if i >= 1 && i <= 10 =>
case i if i >= 11 && i <= 20 =>
But then I guess an if-else if-else block is more readable.
This is called guards:
case x if (x > 0) => OK ...