使用下划线构造lambda表达式

时间:2014-12-10 12:53:33

标签: scala lambda anonymous-function

此代码

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

执行我期望的事情:它会将1打印到30,并穿插空白。我很清楚我在这里发生了什么,我想:我传递一个首先打印其参数的匿名函数,然后打印一个空行。

我不明白为什么这不做同样的事情:

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

它看起来与我相同。下划线应代表该函数的第一个也是唯一的参数;并且该函数打印其参数,然后打印一个空行。但是当我运行第二个版本时,我没有得到空白。

造成这种差异的原因是什么?

3 个答案:

答案 0 :(得分:5)

第一个变体很简单:

  1. 在第一行中,在println上应用x
  2. 在第二行中,应用无参数println打印额外的换行符)。
  3. 使用第二个变体,您可以有效地告诉Scala:

    1. 在第一行中,从println()定义一个函数对象。 随后,对这个新创建的对象不执行任何操作。
    2. 在第二行中,将println应用于参数(元素) 序列)。
    3. 混淆源于假设println(x)println _是等价的。 它们不同。 funcId _语法定义了基于funcId的新函数,它与使用"下划线参数"不同。调用函数时的表示法。

答案 1 :(得分:3)

这里有很多事情要发生。

首先,所有参数占位符语法只能在lambda定义的外括号内使用。它不能在lambda定义中执行的方法调用的括号内使用。

这是一个证明这一点的例子。

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

这样可行。

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

这不起作用。

因此,您的代码根本不使用参数占位符语法。它改为使用部分应用的函数。

例如

(1 to 10).foreach(println _)

在功能上等于

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

此外,当在lambda表达式中使用方法名称时,可以省略下划线。 Scala仍将生成部分应用的方法。

因此

(1 to 10).foreach(println)

等于

(1 to 10).foreach(println _)

因此您的代码等于

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

因为{a a}返回a,它等于

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

答案 2 :(得分:1)

要添加到其他答案,实际上有一种方法可以使用println(_)而不是声明x参数:

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

这里foreach参数是函数,它首先为范围元素调用println(_),然后将println(Int)结果((): Unit)传递给另一个函数{{1忽略它的参数并打印新行。