I came from What is the formal difference in Scala between braces and parentheses, and when should they be used?
, so still I am not clear understand the scala method call syntax trick. I think the lcn's answer in the link is clear but what does these mean:
val r = List(1, 2, 3).foldLeft(0) _+_ //r: String => String = $Lambda$1642/1200068083#562c1af9
val l = r{"hello"} //l: String = $Lambda$1643/1458648440#23ae6386hello
why r is a function? and call it return another function l and can not call l?
are there some official doc for method call in () and {}?
This has to do with operator precedence:
val r = List(1, 2, 3).foldLeft(0) _+_
Is equivalent to:
val r = (List(1, 2, 3).foldLeft(0) _) + _
Which means - we take the partially applied foldLeft function, List(1, 2, 3).foldLeft(0) _, which has the type ((Int, Int) => Int) => Int), and we call + _ on it.
Now - Scala compiler tries to figure out what the + operator should mean for a Function, and since Function does not define such an operator, it does the best it can to find a suitable implicit conversion, and finds that indeed the Function can be converted to a String using its toString method, so that the + _ means "a new anonymous function that appends any input String passed to the String representation of the List(1, 2, 3).foldLeft(0) _ function".
So we end up with a String => String function, that appends the input to the toString result of the function List(1, 2, 3).foldLeft(0) _, which is "<function1>":
scala> val r = List(1, 2, 3).foldLeft(0) _ + _
r: String => String = <function1>
scala> r("a")
res5: String = <function1>a
scala> r(" bbbb")
res6: String = <function1> bbbb
Related
I thought in scala map(f) is the same as map(_.f) as map(x => x.f), but turns out it is not
scala> val a = List(1,2,3)
val a: List[Int] = List(1, 2, 3)
scala> a.map(toString)
val res7: List[Char] = List(l, i, n)
scala> a.map(_.toString)
val res8: List[String] = List(1, 2, 3)
What happenes when a.map(toString) is called? Where did the three charaacters l, i, and n come from?
map(f) is not the same as map(_.f()). It's the same as map(f(_)). That is, it's going to call f(x), not x.f(), for each x in the list.
So a.map(toString) should be an error because the normal toString method does not take any arguments. My guess is that in your REPL session you've defined your own toString method that takes an argument and that's the one that's being called.
I tried to make my own map method in Scala and this is the result:
def map[A, B](f: A => B)(as: List[A]): List[B] =
for (i <- as) yield f(i)
And if I try to convert a List[Int] to a List[String]:
val list = List(1, 2, 3, 4, 5) //> list : List[Int] = List(1, 2, 3, 4, 5)
map(toString()) (list) //> res0: List[Char] = List(e, b, u, n, g)
I get the same result if I try:
list.map(toString()) //> res1: List[Char] = List(e, b, u, n, g)
My Question: Why gives toString() not a List("1","2"...)?
You're calling toString() on the current object (which apparently returns something like "?ebung...") and passing the resulting string as an argument to map.
So you might expect that to produce a type error because map expects a function, but you're passing a string. However strings count as functions too (because RichString implements the Function1[Int, Char] trait) that take an integer and return the character at that index. So that's why you get a list containing those characters.
To do what you meant to do (create a function that calls toString on its argument), you can either use x => x.toString or _.toString for short.
This question already has answers here:
Scala underscore minimal function
(3 answers)
Closed 6 years ago.
not able to type underscore in question. The code snippet has underscore though.
why list.map(+2) works in place of list.map(x=>x+2) but list.map() doesnt work in place of list.map(x=>x)?
scala> val list = List(1,2,3,4)
list: List[Int] = List(1, 2, 3, 4)
scala> list.map(_+2)
res14: List[Int] = List(3, 4, 5, 6)
scala> list.map(x=>x+2)
res15: List[Int] = List(3, 4, 5, 6)
scala> list.map(x=>x)
res16: List[Int] = List(1, 2, 3, 4)
scala> list.map(_)
<console>:13: error: missing parameter type for expanded function ((x$1) => list.map(x$1))
list.map(_)
^
scala>
It's pretty simple. If _ was considered a valid expression for anonymous function and translated to x => x, then _ * 2 would be translated to (x => x) * 2, which would make placeholders useless. Instead Scala is trying to find the first complete expression going "outwards" from _. In case of list.map(_) it's list.map(_), so you have x => list.map(x).
More interesting examples:
_ + (2 * _) //> x => x + (y => 2 * y)
_ + 2 * _ //> (x, y) => x + 2 * y
(parentheses "complete" expression)
For this you need to use the identity function. That's because in this case, the placeholder _ makes your expression list.map(_) a partially applied function. Maybe it makes it clearer if you assign it to a val:
val fun: (Int => Int) => List[Int] = list.map(_)
You could then use your val fun like so:
fun(_ * 2) // returns List(2, 4, 6, 8)
If you want to map to the same value, you can use identity:
list.map(identity) // returns List(1, 2, 3, 4)
The _ in what you're describing is placeholder syntax for anonymous functions and represents an argument to a function.
E.g:
list.map(_+2) // map takes a function which takes one argument and returns that arg + 2
Since map takes a function as an argument, and not a placeholder, that is why you're seeing this issue.
Snippet 1:
val l = List(1,2,43,4)
l.map(i => i *2)
Snippet 2:
val s = "dsadadaqer12"
val g = s.groupBy(c=>c)
g.map ( {case (c,s) => (c,s.length)})
In snippet #2, the syntax different than #1 , i.e. curly braces required -- why?
I thought the following would compile, but it does not:
g.map ( (c,s) => (c,s.length))
Can someone explain why?
Thanks
The difference between the two is - the latter uses Pattern Matching and the former doesn't.
The syntax g.map({case (c,s) => (c,s.length)}) is just syntax sugar for:
g.map(v => v match { case (c,s) => (c,s.length) })
Which means: we name the input argument of our anonymous function v, and then in the function body we match it to a tuple (c,s). Since this is so useful, Scala provides the shorthand version you used.
Of course - this doesn't really have anything to do with whether you use a Map or a List - consider all the following possibilities:
scala> val l = List(1,2,43,4)
l: List[Int] = List(1, 2, 43, 4)
scala> l.map({ case i => i*2 })
res0: List[Int] = List(2, 4, 86, 8)
scala> val l2 = List((1,2), (3,4))
l2: List[(Int, Int)] = List((1,2), (3,4))
scala> l2.map({ case (i, j) => i*j })
res1: List[Int] = List(2, 12)
scala> val g = Map(1 -> 2, 3 -> 4)
g: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 4)
scala> g.map(t => t._1 * t._2)
res2: scala.collection.immutable.Iterable[Int] = List(2, 12)
Both Map and List can use both syntax options, depending mostly on what you actually want to do.
1- g.map{case (c,s) => (c,s.length)}
2- g.map((c,s) => (c,s.length))
The map method pulls a single argument, a 2-tuple, from the g collection. The 1st example compiles because the case statement uses pattern matching to extract the tuple's elements whereas the 2nd example doesn't and it won't compile. For that you'd have to do something like: g.map(t => (t._1, t._2.length))
As for the parenthesis vs. curly braces: braces have always been required for "partial functions," which is what that case statement is. You can use either braces or parens for anonymous functions (i.e. x => ...) although you are required to use braces if the function is more than a single line (i.e. has a carriage-return).
I read somewhere that this parens/braces distinction might be relaxed but I don't know if that's going to happen any time soon.
I'm trying to add another string to Iterable[String] for easy concatenation, but the result is not what I expect.
scala> val s: Iterable[String] = "one string" :: "two string" :: Nil
s: Iterable[String] = List(one string, two string)
scala> s.mkString(";\n")
res3: String =
one string;
two string
scala> (s ++ "three").mkString(";\n")
res5: String =
one string;
two string;
t;
h;
r;
e;
e
How should I should I rewrite this snippet to have 3 string in my iterable?
Edit: I should add, that order of items should be preserved
++ is for collection aggregation. There is no method +, :+ or add in Iterable, but you can use method ++ like this:
scala> (s ++ Seq("three")).mkString(";\n")
res3: String =
one string;
two string;
three
The ++ function is waiting for a Traversable argument. If you use just "three", it will convert the String "three" to a list of characters and append every character to s. That's why you get this result.
Instead, you can wrap "three" in an Iterable and the concatenation should work correctly :
scala> (s ++ Iterable[String]("three")).mkString(";\n")
res6: String =
one string;
two string;
three
I like to use toBuffer and then +=
scala> val l : Iterable[Int] = List(1,2,3)
l: Iterable[Int] = List(1, 2, 3)
scala> val e : Iterable[Int] = l.toBuffer += 4
e: Iterable[Int] = ArrayBuffer(1, 2, 3, 4)
or in your example:
scala> (s.toBuffer += "three").mkString("\n")
I have no idea why this operation isn't supported in the standard library. You can also use toArray but if you add more than one element this will be less performant - I would assume - as the buffer should return itself if it another element is added.