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

最佳答案

这个:

"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)

10-06 13:41