Scala foreach奇怪的行为

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2173373/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 01:53:13  来源:igfitidea点击:

Scala foreach strange behaviour

scala

提问by F0RR

I want to iterate over a list of values using a beautiful one-liner in Scala.

我想在 Scala 中使用漂亮的单行来迭代一系列值。

For example, this one works well:

例如,这个效果很好:

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

scala> x foreach println
1
2
3
4

But if I use the placeholder _, it gives me an error:

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

scala> x foreach println(_ + 1)
<console>:6: error: missing parameter type for expanded function ((x) =>x.$plus(1))
       x foreach println(_ + 1)
                         ^

Why is that? Can't compiler infer type here?

这是为什么?编译器不能在这里推断类型吗?

回答by Daniel C. Sobral

This:

这:

x foreach println(_ + 1)

is equivalent to this:

相当于:

x.foreach(println(x => x + 1))

There's no indication as to what might be the type of x$1, and, to be honest, it doesn't make any sense to print a function.

没有迹象表明 可能是什么类型x$1,而且老实说,打印函数没有任何意义。

You obviously (to me) meant to print x$0 + 1, where x$0would the the parameter passed by foreach, instead. But, let's consider this... foreachtakes, as a parameter, a Function1[T, Unit], where Tis the type parameter of the list. What you are passing to foreachinstead is println(_ + 1), which is an expression that returns Unit.

显然,你(我)的意思来打印x$0 + 1,其中x$0将在参数过去了foreach,取而代之。但是,让我们考虑一下...foreach作为参数, a Function1[T, Unit],其中T是列表的类型参数。你传递给的foreachprintln(_ + 1),这是一个返回的表达式Unit

If you wrote, instead x foreach println, you'd be passing a completely different thing. You'd be passing the function(*) println, which takes Anyand returns Unit, fitting, therefore, the requirements of foreach.

相反x foreach println,如果你写了 ,你会传递一个完全不同的东西。您将传递 function(*) println,它接受Any并返回Unit,因此适合foreach.

This gets slightly confused because of the rules of expansion of _. It expands to the innermost expression delimiter (parenthesis or curly braces), except if they are in place of a parameter, in which case it means a different thing: partial function application.

由于 的扩展规则,这有点令人困惑_。它扩展到最里面的表达式分隔符(括号或花括号),除非它们代替参数,在这种情况下,它意味着不同的东西:部分函数应用。

To explain this better, look at these examples:

为了更好地解释这一点,请查看以下示例:

def f(a: Int, b: Int, c: Int) = a + b + c
val g: Int => Int = f(_, 2, 3) // Partial function application
g(1)

Here, we applies the second and third arguments to f, and returned a function requiring just the remaining argument. Note that it only worked as is because I indicated the type of g, otherwise I'd have to indicate the type of the argument I was not applying. Let's continue:

在这里,我们将第二个和第三个参数应用于f,并返回一个只需要剩余参数的函数。请注意,它仅按原样工作,因为我指明了 的类型g,否则我必须指明我没有应用的参数的类型。让我们继续:

val h: Int => Int = _ + 1 // Anonymous function, expands to (x: Int => x + 1)
val i: Int => Int = (_ + 1) // Same thing, because the parenthesis are dropped here
val j: Int => Int = 1 + (_ + 1) // doesn't work, because it expands to 1 + (x => x + 1), so it misses the type of `x`
val k: Int => Int = 1 + ((_: Int) + 1) // doesn't work, because it expands to 1 + (x: Int => x + 1), so you are adding a function to an `Int`, but this operation doesn't exist

Let discuss kin more detail, because this is a very important point. Recall that gis a function Int => Int, right? So, if I were to type 1 + g, would that make any sense? That's what was done in k.

让我们k更详细地讨论一下,因为这是非常重要的一点。回想一下,这g是一个函数Int => Int,对吧?那么,如果我要输入1 + g,那有意义吗?这就是在k.

What confuses people is that what they really wanted was:

让人们困惑的是,他们真正想要的是:

val j: Int => Int = x => 1 + (x + 1)

In other words, they want the x$1replacing _to jump to outsidethe parenthesis, and to the proper place. The problem here is that, while it may seem obvious to them what the proper place is, it is not obvious to the compiler. Consider this example, for instance:

换句话说,他们希望x$1替换_跳到括号,并跳到适当的位置。这里的问题是,虽然对他们来说正确的位置似乎很明显,但对编译器来说却并不明显。考虑这个例子,例如:

def findKeywords(keywords: List[String], sentence: List[String]) = sentence.filter(keywords contains _.map(_.toLowerCase))

Now, if we were to expand this to outside the parenthesis, we would get this:

现在,如果我们将其扩展到括号​​之外,我们将得到:

def findKeywords(keywords: List[String], sentence: List[String]) = (x, x) => sentence.filter(keywords contains x.map(x.toLowerCase))

Which is definitely not what we want. In fact, if the _did not get bounded by the innermost expression delimiter, one could never use _with nested map, flatMap, filterand foreach.

这绝对不是我们想要的。事实上,如果_没有受到最里面的表达式定界符的限制,就永远不能使用_嵌套的mapflatMapfilterforeach

Now, back to the confusion between anonymous function and partial application, look here:

现在,回到匿名函数和部分应用程序之间的混淆,看这里:

List(1,2,3,4) foreach println(_) // doesn't work
List(1,2,3,4) foreach (println(_)) // works
List(1,2,3,4) foreach (println(_ + 1)) // doesn't work

The first line doesn't work because of how operation notation works. Scala just sees that printlnreturns Unit, which is not what foreachexpects.

由于操作符号的工作方式,第一行不起作用。Scala 只是看到printlnreturn Unit,这不是foreach预期的。

The second line works because the parenthesis let Scala evaluate println(_)as a whole. It is a partial function application, so it returns Any => Unit, which is acceptable.

第二行之所以有效,是因为括号让 Scala 将其println(_)作为一个整体进行评估。是偏函数应用,所以返回Any => Unit,可以接受。

The third line doesn't work because _ + 1is anonymous function, which you are passing as a parameter to println. You are notmaking printlnpart of an anonymous function, which is what you wanted.

第三行不起作用,因为_ + 1是匿名函数,您将其作为参数传递给println. 您没有成为println匿名函数的一部分,而这正是您想要的。

Finally, what few people expect:

最后,很少有人期望:

List(1,2,3,4) foreach (Console println _ + 1)

This works. Why it does is left as an exercise to the reader. :-)

这有效。为什么会这样,留给读者作为练习。:-)

(*) Actually, printlnis a method. When you write x foreach println, you are not passing a method, because methods can't be passed. Instead, Scala creates a closure and passes it. It expands like this:

(*) 其实println是一种方法。当你写的时候x foreach println,你不是在传递一个方法,因为方法不能被传递。相反,Scala 创建一个闭包并传递它。它像这样扩展:

x.foreach(new Function1[Any,Unit] { def apply(x: Any): Unit = Console.println(x) })

回答by davetron5000

The underscore is a bit tricky. According to the spec, the phrase:

下划线有点棘手。根据规范,这句话:

_ + 1

is equivalent to

相当于

x => x + 1

Trying

x foreach println (y => y + 1)

yields:

产量:

<console>:6: error: missing parameter type
           x foreach println (y => y + 1)

If you add some types in:

如果你添加一些类型:

x foreach( println((y:Int) => y + 1))
<console>:6: error: type mismatch;
 found   : Unit
 required: (Int) => Unit
           x foreach( println((y:Int) => y + 1))

The problem is that you are passing an anonymous function to printlnand it's not able to deal with it. What you really want to do (if you are trying to print the successor to each item in the list) is:

问题是您将匿名函数传递给println它,但它无法处理它。您真正想要做的(如果您尝试打印列表中每个项目的后继)是:

x map (_+1) foreach println

回答by Viktor Klang

scala> for(x <- List(1,2,3,4)) println(x + 1)
2
3
4
5

回答by Alexey

There is a strange limitation in Scala for the nesting depth of expressions with underscore. It's well seen on the following example:

Scala 中对于带下划线的表达式的嵌套深度有一个奇怪的限制。在以下示例中很明显:

 scala> List(1) map(1+_)
 res3: List[Int] = List(2)

 scala> Some(1) map (1+(1+_))
 <console>:5: error: missing parameter type for expanded function ((x) => 1.+(x))
        Some(1) map (1+(1+_))
                     ^

Looks like a bug for me.

对我来说看起来像是一个错误。

回答by Randall Schulz

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(_))
1
2
3