Scala foreach奇怪的行为

我想在Scala中使用漂亮的单行代码遍历值列表。


例如,这很好用:


scala> val x = List(1,2,3,4)

x: List[Int] = List(1, 2, 3, 4)


scala> x foreach println

1

2

3

4

但是,如果我使用占位符_,则会给我一个错误:


scala> x foreach println(_ + 1)

<console>:6: error: missing parameter type for expanded function ((x$1) =>x$1.$plus(1))

       x foreach println(_ + 1)

                         ^


LEATH
浏览 1050回答 3
3回答

慕雪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) })

拉风的咖菲猫

下划线有点棘手。根据规范,该短语:_ + 1相当于x => x + 1试x foreach println (y => y + 1)产量:<console>:6: error: missing parameter type&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;x foreach println (y => y + 1)如果您在其中添加一些类型:x foreach( println((y:Int) => y + 1))<console>:6: error: type mismatch;&nbsp;found&nbsp; &nbsp;: Unit&nbsp;required: (Int) => Unit&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;x foreach( println((y:Int) => y + 1))问题是您要传递一个匿名函数给println它,而它不能处理它。您真正想要做的是(如果您尝试将继承者打印到列表中的每个项目上):x map (_+1) foreach println

喵喵时光机

Welcome to Scala version 2.8.0.Beta1-prerelease (Java HotSpot(TM) Client VM, Java 1.6.0_17).Type in expressions to have them evaluated.Type :help for more information.scala> val l1 = List(1, 2, 3)l1: List[Int] = List(1, 2, 3)scala>scala> l1.foreach(println(_))123
打开App,查看更多内容
随时随地看视频慕课网APP