How to use placeholder syntax properly with map? - scala

I am trying to use placeholder syntax for simple map:
Array(1,2,3).map(if(_ % 2 == 0) _ * 2)
I was expecting to have the same effect as:
Array(1,2,3).map(i=>if (i%2==0) i * 2)
It complains
error: type mismatch;
found : Unit
required: Int => ?
I also tried:
Array(1,2,3).map(if(_ % 2 == 0) _ * 2 else _) //with else
Array(1,2,3).map(if((_:Int) % 2 == 0) (_:Int) * 2 else (_:Int)) //All typed
Array(1,2,3).map(if((_:Int) % 2 == 0) 0 else 1) //Typed and specific return Int
Every one of them gives error. How to properly use this syntax in this case?
Edit
The link states that filter(_:Boolean) and filter (_ == true) should work, but my trials with specific typing does not work. This link also states that if (_) x else y should work, but in my case it does not. Need more explanation.
Edit 2
Tried:
Array(true,false).map(if(_) 0 else 1)
It works. But my case:
Array(1,2,3).map(if((_) % 2 == 0) 0 else 1)
Does not work.
Does this syntax only support such simple expressions?

Re: your original question:
Array(1, 2, 3).map(if (_ % 2 == 0) _ * 2)
// error: missing parameter type for expanded function ...
// error: type mismatch ...
The _ placeholder used in such pattern represents positional parameters of an anonymous function, hence if (_ % 2 == 0) _ * 2 is equivalent to:
if (x => x % 2 == 0) y => y * 2
That explains the missing parameter error. The type mismatch error is due to missing else in the if statement, forcing the compiler to return a Unit:
Array(1, 2, 3).map(i => if (i % 2 == 0) i * 2 else i)
// res1: Array[Int] = Array(1, 4, 3
Given that you need to specify the single input multiple times I'm not sure it'll work for you.
As you've already noticed, this placeholder syntax does have its limitation and should be treated as a convenient shortform for relatively simple cases. Like the if (_ % 2 == 0) 0 else 1) case, nesting _ within a map generally does not work well. For example:
List("a", "b", "c").map(_ * 3)
// res2: List[String] = List(aaa, bbb, ccc)
List("a", "b", "c").map((_ + " ") * 3)
// error: missing parameter type for expanded function ...
For more usage re: _, here is a SO link, and a Scala doc about commonly used symbols including _.

Related

Unpack list of tuples to check that middle value in each tuple doesn't exceed a certain amount

I've been trying unpack each tuple in a list of tuples and check to see that the middle value in each tuple doesn't exceed a certain amount in an equation. I keep getting ") expected but => found." I'm already pushing my knowledge by attempting this, so I have no idea what is wrong?
def howmuch(m: Int, n: Int): List[Any] = {
val a = ((m-1) to (n-1)).toList
val b = a.map(i =>
if (((7 / 9.0 * i + 1 / 9.0) % 1) == 0) (7*i+2,i,(7 / 9.0 * i + 1 / 9.0).toInt) else n+1)
val c = b.map((i._1,i._2,i._3) => if (9*(i._3)+1 <=n) (i._1,i._2,i._3) else n+1)
c
}
The first rule of if/else is that the types of both sides should match.
Let's look at the first one (reformatted to make it easier to read).
a.map(i =>
if (((7 / 9.0 * i + 1 / 9.0) % 1) == 0)
(7*i+2, i, (7 / 9.0 * i + 1 / 9.0).toInt) //(Int,Int,Int) tuple
else n+1 //Int
)
So if a is something like List(5,6,7) then the result will be List(8, (37,5,4), 8). This a list of type List[Any]. That's a very bad sign. Whenever you encounter type Any in Scala code it should raise a red flag.
Next you're trying to map() over this list. Not only do you get the syntax all wrong, each element is type Any. You can't treat each element as if it were a tuple because it's not a Tuple, it's an Any. You can't add 1 to an element because it's not an Int, it's an Any.
Time to go back and rethink this from the beginning.

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

How to Map Partial Elements in Scala/Spark

I have a list of integers:
val mylist = List(1, 2, 3, 4)
What I want to do is to map the element which are even numbers in mylist, and multiply them by 2.
Maybe the code should be:
mylist.map{ case x%2==2 => x*2 }
I expect the result to be List(4, 8) but it's not. What is the correct code?
I know I could realize this function by using filter + map
a.filter(_%2 == 0).map(_*2)
but is there some way to realize this function by only using map()?
map does not reduce number of elements in transformation.
filter + map is right approach.
But if single method is needed, use collect:
mylist.collect{ case x if x % 2 == 0 => 2 * x }
Edit:
withFilter + map is more efficient than filter + map (as withFilter does not create intermediate collection, i.e. it works lazily):
mylist.withFilter(_ % 2 == 0).map(_ * 2)
which is same as for :
for { e <- mylist if (e % 2 == 0) } yield 2 * e

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

how to simplify scala's function literal like this?

I'm new to scala and trying to write a function literal that check whether a given integer is odd or not.
my first attempt is:
val isOdd = (x:Int) => (x & 1) == 1
it works great, and, since the parameter x only appears once within this function literal, I'm tempted to use the "_" notation to simplify it further, like this:
val isOdd = ((_:Int) & 1 ) == 1
however this time the compiler complains :
warning: comparing a fresh object using `==' will always yield false
val isOdd = ((_:Int) & 1 ) == 1
what does this warning mean? why does the compiler recognize ((_ :Int) & 1) as fresh object rather than a bitwise operation that results in a value? is there any way to write this function literal using the "_" notation?
The problem is basically that Scala needs to tell the difference between
val isOdd = ((_:Int) & 1 ) == 1
where you want everything to the right of the equals sign to be a lambda, and
val result = collection.map( _ + 1 )
where you want only the stuff inside the parentheses to be a lambda
Scala has decided that when you use the underscore to create a lambda, that it's going to pick the innermost set of parentheses as the boundaries of that lambda. There's one exception: (_:Int) doesn't count as the innermost parentheses because its purpose is only to group they type declaration with the _ placeholder.
Hence:
val isOdd = ((_:Int) & 1 ) == 1
^^^^^^^^^^^^^^
this is the lambda
val result = collection.map( _ + 1 )
^^^^^^^
this is the lambda
val result = collection.map(( _ + 1) / 2)
^^^^^^^^
this is the lambda
and the compiler can't infer the type of the _
val result = somemap.map(( _ + 1) / 2 * _)
^^^^^^^^
this is an inner lambda with one parameter
and the compiler can't infer the type of the _
^^^^^^^^^^^^^^^^^
this is an outer lambda with one parameter
This last case lets you do things like
_.map(_ + 1)
and have that get translated into
x => x.map( y=> y + 1 )
Only slightly cheating:
val isOdd = (_: Int) % 2 == 1
:-)
There you go:
val isOdd = ((_: Int) & 1) andThen (1 ==)
What Scala is doing is this:
it sees ((_:Int) & 1 ) and creates an object of type (Int) => Int, that is, a function.
it then applies the comparison operator == to compare this function to the value 1
A function is not equal to the value 1. Therefore the result is false, so your code is equivalent to:
val isOdd = false
What you could do is create another anonymous function that does the == 1 part of your computation. This is ugly:
val isOdd = ((_: Int) & 1)(_: Int) == 1
This is equivalent to the more verbose (and perhaps easier to understand):
val isOdd = (x: Int) => 1 == ((_: Int) & 1)(x)
A different approach
val isOdd = (_:Int).&(1) == 1