" Hello"之间的区别是什么? foreach(x = x * _.toLong)和"你好" scala中的foreach(x * = _.toLong)?

时间:2016-08-14 08:44:56

标签: scala

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

2 个答案:

答案 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 *= yx = 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的简写形式,这就是它的工作原理。