Constructing a lambda expression using underscore

This code

(1 to 30).foreach { x => println(x) println } 

does what I expect: it prints each of 1 to 30 interspersed with spaces. I understand quite clearly what is going on here, I think: I am passing an anonymous function that first prints its argument and then prints an empty string.

I do not understand why this does not do the same:

 (1 to 30).foreach { println _ println } 

It looks like me. The underscore should represent the first and only argument to the function; and the function prints its argument and then prints an empty string. But when I run this second version, I do not get blank lines.

What causes this difference?

+5
source share
3 answers

The first option is simple:

  • On the first line, apply println to x .
  • In the second line, apply the argument no-argument println ( which prints an additional line of a new line ).

In the second option, you really tell Scala to do this:

  • On the first line, define the function object from println() . Subsequently, do nothing with this newly created object.
  • In the second line, apply println to the argument (element sequence).

The confusion stems from the assumption that println(x) and println _ equivalent. They are different. The syntax funcId _ defines a new function based on funcId , it is not the same as using the argument underscore notation when calling a function.

+5
source

Here are a few things going on.

First, of all the syntax of the parameter builder, you can use it only in the outer parentheses of the lambda definition. It cannot be used in parentheses of method calls that you execute in a lambda definition.

Here is an example to demonstrate this point.

 val a = (1 to 10).map(_ + 1) 

This will work.

 val b = (1 to 10).map(math.sin(_ + 1)) 

This will not work.

Therefore, your code does not use the parameter linker syntax at all. Instead, it uses partially applied functions.

for instance

 (1 to 10).foreach(println _) 

functionally equal

 val a = println (_ : Int) (1 to 10).foreach(a) 

Also, when a method name is used in a lambda expression, the underscore may be omitted. Scala will still generate a partially applicable method.

therefore

 (1 to 10).foreach(println) 

equally

 (1 to 10).foreach(println _) 

And so your code is equal

 val a = println (_ : Int) (1 to 10).foreach{ a a } 

And since {aa} returns a, it equals

 val a = println (_ : Int) (1 to 10).foreach(a) 
+3
source

To add to the other answers, there really is a way to use println(_) rather than declaring the x parameter:

 (1 to 30).foreach { (println(_: Int)) .andThen(_ => println) } 

Here, the foreach parameter is a function that first calls println(_) for a range element, and then passes the result of println(Int) (which is (): Unit ) to another _ => println function, which ignores its argument and prints a new line.

0
source

Source: https://habr.com/ru/post/1208733/


All Articles