Scala foreach奇怪的行为 [英] Scala foreach strange behaviour

查看:42
本文介绍了Scala foreach奇怪的行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I want to iterate over a list of values using a beautiful one-liner in 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

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

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$1) =>x$1.$plus(1))
       x foreach println(_ + 1)
                         ^

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

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

推荐答案

这个:

x foreach println(_ + 1)

相当于:

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

没有说明 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$0 + 1,其中 x$0foreach 传递的参数,而不是.但是,让我们考虑一下... foreachFunction1[T, Unit] 作为参数,其中 T 是类型参数的名单.您传递给 foreach 的是 println(_ + 1),这是一个返回 Unit 的表达式.

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

如果你写的是x foreach println,你会传递一个完全不同的东西.您将传递函数(*) println,它接受Any 并返回Unit,因此符合 的要求foreach.

If you wrote, instead x foreach println, you'd be passing a completely different thing. You'd be passing the function(*) println, which takes Any and returns Unit, fitting, therefore, the requirements of 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)

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

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:

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 here
val 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 中所做的.

Let discuss k in more detail, because this is a very important point. Recall that g is 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.

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

What confuses people is that what they really wanted was:

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

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

In other words, they want the x$1 replacing _ to jump to outside the 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:

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$1, x$2) => sentence.filter(keywords contains x$1.map(x$2.toLowerCase))

这绝对不是我们想要的.事实上,如果 _ 没有被最里面的表达式定界符限制,那么永远不能将 _ 与嵌套的 map 一起使用,flatMapfilterforeach.

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, filter and foreach.

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

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

由于操作符号的工作方式,第一行不起作用.Scala 只是看到println 返回Unit,这不是foreach 所期望的.

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

第二行之所以有效,是因为括号让 Scala 将 println(_) 作为一个整体来计算.它是一个偏函数应用程序,所以它返回 Any =>;单位,这是可以接受的.

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.

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

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

最后,很少有人期待:

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

这有效.为什么这样做留给读者作为练习.:-)

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

(*) 实际上,println 是一个方法.当你写 x foreach println 时,你并没有传递一个方法,因为方法不能传递.相反,Scala 创建一个闭包并传递它.它像这样展开:

(*) Actually, println is 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:

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

这篇关于Scala foreach奇怪的行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆