2

我已经阅读了其他一些问题,例如Scala 中下划线的所有用途是什么?虽然我确定这个问题已经被问过了,但我无法解决所有其他 17000 个 Scala 问题。

Foreach 有奇怪的行为,并且Placeholder 没有用,但它似乎仍然是一个隐藏的功能

scala> val is = (1 to 5) toList
is: List[Int] = List(1, 2, 3, 4, 5)

scala> is foreach { i => println("Hi.") ; Console println 2 * i }
Hi.
2
Hi.
4
Hi.
6
Hi.
8
Hi.
10

scala> is foreach { println("Hi.") ; Console println 2 * _ }
Hi.
2
4
6
8
10

有人可以解释一下区别吗?

如果您感到一阵热情,请尝试:

scala> is foreach { i => println("Hi!") ; Console println 2 * i }
java.lang.IllegalArgumentException: !") ; Console println 2 * i }: event not found

然后看到这个答案。 是的,这确实发生了。

4

2 回答 2

7

占位符语法适用于一个表达式,而不适用于整个块,因此您的示例被解释为{ println("Hi."); i => Console println 2 * i }

于 2013-07-15T06:40:01.303 回答
3

我认为,Landei 有正确的答案,但它并没有完全解开。让我们从最后开始:

scala> Console println 2 * _
<console>:8: error: missing parameter type for expanded function
((x$1) => Console.println(2.$times(x$1)))
              Console println 2 * _
                                  ^

好的,所以我们看到它本身Console println 2 * _正在尝试通过显式 eta 扩展创建一个函数,但它不知道返回参数类型,所以它不能。

现在让我们尝试一个返回函数的代码块。

scala> { println("Hi"); (i: Int) => i*5 }
Hi
res1: Int => Int = <function1>

因此,与所有事情一样,您执行整个块(包括副作用语句,如println),并返回作为您的函数的返回值。

现在,正如 Landei 所说,占位符语法仅适用于一个(简单)表达式中的一个参数,而在第二种情况下,我们没有一个简单的表达式,而是一个块表达式(由两个简单的表达式组成)。所以我们没有使用占位符语法,我们正在创建一个函数。我们在代码块中执行此操作:

is foreach { println("Hi.") ; Console println 2 * _ }

由于我们不是从函数参数开始的,因此它被解释为一个普通参数,除了我们几乎可以在任何上下文中——包括参数列表——用(x)块表达式替换一个简单的表达式{ stuff; x }。所以我们可以把它想象成

is foreach ({ println("Hi.") ; Console.println 2 * _ })

现在类型推断器知道返回类型应该是什么,所以它运行打印出“Hi”并创建一个函数的代码块,然后将该函数(仅一次,在开始时!)传递给foreach. 如果类型推断器可以跨行查找类型,则相当于:

val temp = { println("Hi."); Console.println 2 * _ }
is foreach (temp)
于 2013-07-15T07:51:44.383 回答