此代码
(1 to 30).foreach { x =>
println(x)
println
}
执行我期望的事情:它会将1
打印到30
,并穿插空白。我很清楚我在这里发生了什么,我想:我传递一个首先打印其参数的匿名函数,然后打印一个空行。
我不明白为什么这不做同样的事情:
(1 to 30).foreach {
println _
println
}
它看起来与我相同。下划线应代表该函数的第一个也是唯一的参数;并且该函数打印其参数,然后打印一个空行。但是当我运行第二个版本时,我没有得到空白。
造成这种差异的原因是什么?
答案 0 :(得分:5)
第一个变体很简单:
println
上应用x
。println
(打印额外的换行符)。使用第二个变体,您可以有效地告诉Scala:
println()
定义一个函数对象。
随后,对这个新创建的对象不执行任何操作。 println
应用于参数(元素)
序列)。混淆源于假设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忽略它的参数并打印新行。