2016-08-14 78 views
3

斯卡拉的"Hello" foreach (x = x * _.toLong)"Hello" foreach (x *= _.toLong)有何区别?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 

回答

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) 
+0

“另请注意,使用副作用和可变值是Scala中的一大难题。” - 这不是真的。 Scala是一种不纯的对象功能语言。你可以愉快地在各处放置副作用,并且类型检查器不会抱怨。 –

+0

我不是说类型检查会抱怨。我只是说,特别是因为它是允许的,它可能导致设计不好,并且不鼓励。如果斯卡拉不是不纯,我的评论将毫无意义,因为语言本身不会允许它。 – slouc

+0

当语言的基础基础允许副作用和变异时,那么你的陈述并不完全正确。即使Odersky本人也记录在案,表明他不是纯粹功能性编程的粉丝(这就是你的陈述所指的),并推出了一个更加宽松的“功能性编程”版本。在我看来,尝试遵守惯例的纯洁性是一场艰苦的战斗,Scala通过不擅长的方式在这里推销某些东西。 –

1

的区别在于使用的是直接的归因=这显然会失败尚属首例。

如果你看一下foreach的签名,你可以看到它期待着一个函数。

在第一种情况下,您根本就没有提供,因为您不尊重lambda语法。 foreach函数预期的f: A => U的lambda语法是x => x * 5或类似的东西。

你显然不这样做,真正的语法会有:

"Hello" foreach (ch => x = x * ch.toLong) 

在第二种情况下,你使用的是简短的λ,即事实col.foreach(x => x + 5)可以重新写为col.foreach(_ + 5),除了您正在将其转换为col.foreach(5 + _),这也是编写lambda的正确形式。

第二个示例中的_正确地采用了字符串中当前字符的形式。

所以这个"Hello" foreach (x *= _.toLong)实际上是"Hello" foreach (ch => x *= ch.toLong),但是使用lambda的简写形式,这就是它工作的原因。

+0

Scala编译器有时可以编译出惊人的代码。 –

相关问题