Scala中"Hello" foreach (x = x * _.toLong)
和"Hello" foreach (x *= _.toLong)
之间的区别是什么?
不能工作:
scala> var x : Long = 1
x: Long = 1
scala> "Hello" foreach (x = x * _.toLong )
<console>:13: error: missing parameter type for expanded function ((x$1) => x.$times(x$1.toLong))
"Hello" foreach (x = x * _.toLong )
使用:
scala> "Hello" foreach (x *= _.toLong )
scala> xbtebh
res89: Long = 9415087488
答案 0 :(得分:4)
这:
"Hello" foreach (x = x * _.toLong)
实际上是由编译器扩展到:
"Hello" foreach (x = x * (x$1 => x$1.toLong))
显然,将Long
与在其参数上调用toLong()
的匿名函数相乘并不是很有意义。当然,自己编写扩展版本可以正常工作,例如"Hello" foreach (y => x = x * y.toLong)
。
在你的第二个表达式"Hello" foreach (x *= _.toLong )
中,编译器需要完成两个扩展:一个扩展下划线,如上例所示,另一个扩展x *= y
成x = x * y
。显然第一个发生在第二个之前,因此编译器将(x *= _.toLong)
视为单个表达式,因此它不会扩展到(x *= (x$1 => x$1.toLong))
,而是扩展为x$1 => (x *= x$1.toLong)
。我无法指出它,因为我需要深入研究Scala规范和编译器内部,但现在你已经了解了导致这种行为的原因。
我个人的建议是仅在琐碎的情况下使用下划线,例如List(1, 2, 3).map(_.toLong)
,并且总是在像你这样的情况下写出完整的功能,例如"Hello" foreach (arg => x = x * arg.toLong)
。
另请注意,在Scala中使用副作用和可变值是一个很大的禁忌。这是代码的改进版本:
val result = "Hello".foldLeft(1: Long)((x, c) => x * c.toLong)
答案 1 :(得分:1)
区别在于您使用=
直接归因的第一种情况,这显然会失败。
如果你看一下foreach
的签名,你可以看到它正在期待一个功能。
在第一个场景中,你根本就没有提供一个,因为你不尊重lambda语法。 f: A => U
函数所期望的foreach
的lambda语法是x => x * 5
或类似的东西。
你显然没有这样做,真正的语法是:
"Hello" foreach (ch => x = x * ch.toLong )
在第二个实例中,您使用的是shortform lambda,即col.foreach(x => x + 5)
可以重写为col.foreach(_ + 5)
的事实,除非您将其反转为col.foreach(5 + _)
,这也是写作lambdas的正确形式。
第二个示例中的_
正确地采用字符串中当前字符的形式。
所以这个"Hello" foreach (x *= _.toLong )
实际上是"Hello" foreach (ch => x *= ch.toLong )
但是使用lambda的简写形式,这就是它的工作原理。