慕雪6442864
这个:x foreach println(_ + 1)等效于此:x.foreach(println(x$1 => x$1 + 1))没有迹象表明的类型可能是什么x$1,并且说实话,打印函数没有任何意义。您(对我而言)显然打算打印x$0 + 1,而x$0参数传递到的位置foreach。但是,让我们考虑一下...... foreach以a作为参数Function1[T, Unit],其中T是列表的类型参数。foreach相反println(_ + 1),您传递给的是返回的表达式Unit。如果您写了,相反x foreach println,您将传递完全不同的东西。您将传递function(*)println,该函数接受Any并返回Unit,因此符合的要求foreach。由于的扩展规则,这有点令人困惑_。它扩展到最里面的表达式定界符(括号或花括号),除非它们代替了参数,在这种情况下,它意味着另一件事:部分函数应用程序。为了更好地解释这一点,请看以下示例:def f(a: Int, b: Int, c: Int) = a + b + cval g: Int => Int = f(_, 2, 3) // Partial function applicationg(1)在这里,我们将第二个和第三个参数应用于f,并返回一个仅需要剩余参数的函数。请注意,它只能按原样工作,因为我指出了的类型g,否则我必须指出未应用的参数的类型。让我们继续:val h: Int => Int = _ + 1 // Anonymous function, expands to (x$1: Int => x$1 + 1)val i: Int => Int = (_ + 1) // Same thing, because the parenthesis are dropped hereval j: Int => Int = 1 + (_ + 1) // doesn't work, because it expands to 1 + (x$1 => x$1 + 1), so it misses the type of `x$1`val k: Int => Int = 1 + ((_: Int) + 1) // doesn't work, because it expands to 1 + (x$1: Int => x$1 + 1), so you are adding a function to an `Int`, but this operation doesn't exist让我们k更详细地讨论,因为这是非常重要的一点。回想一下这g是一个函数Int => Int,对吗?所以,如果我要输入1 + g,这有意义吗?这就是在中所做的k。使人们感到困惑的是他们真正想要的是:val j: Int => Int = x$1 => 1 + (x$1 + 1)换句话说,他们希望x$1替换对象_跳到括号之外,并跳到正确的位置。这里的问题是,尽管对他们来说合适的地方似乎很明显,但对于编译器来说却并不明显。考虑以下示例,例如:def findKeywords(keywords: List[String], sentence: List[String]) = sentence.filter(keywords contains _.map(_.toLowerCase))现在,如果将其扩展到括号之外,我们将得到:def findKeywords(keywords: List[String], sentence: List[String]) = (x$1, x$2) => sentence.filter(keywords contains x$1.map(x$2.toLowerCase))这绝对不是我们想要的。事实上,如果_没有得到由最里面的表达式分隔符为界,一个永远无法使用_嵌套map,flatMap,filter和foreach。现在,回到匿名函数和部分应用程序之间的混淆,请看这里:List(1,2,3,4) foreach println(_) // doesn't workList(1,2,3,4) foreach (println(_)) // worksList(1,2,3,4) foreach (println(_ + 1)) // doesn't work由于操作符号的工作方式,第一行不起作用。Scala只是看到printlnreturn Unit,这不是foreach期望的。第二行之所以起作用,是因为括号使Scala println(_)可以整体评估。这是一个部分函数应用程序,因此它返回Any => Unit,这是可以接受的。第三行不起作用,因为它_ + 1是匿名函数,您将其作为参数传递给println。您并没有成为println匿名函数的一部分,而这正是您想要的。最后,很少有人期望:List(1,2,3,4) foreach (Console println _ + 1)这可行。为什么这样做留给读者练习。:-)(*)实际上println是一种方法。在编写时x foreach println,您没有在传递方法,因为无法传递方法。相反,Scala创建一个闭包并将其传递。它像这样扩展:x.foreach(new Function1[Any,Unit] { def apply(x$1: Any): Unit = Console.println(x$1) })