List.filter中的下划线

时间:2011-10-08 06:50:13

标签: scala

为什么这不起作用:

List(true,false).filter(_).size

错误说:

<console>:8: error: missing parameter type for expanded function 
((x$1) => List(true, false).filter(x$1).size)
    List(true,false).filter(_).size
                            ^

但以下工作(对我来说看起来一样):

List(true,false).filter(a => a).size

我正在使用Scala 2.9.0.1。

3 个答案:

答案 0 :(得分:23)

在Scala中处理_有点棘手,作为旁注,我认为错误处理应该有所改进。回到主题,看看这个例子:

def twice(i: Int) = i * 2
def gt2(j: Int) = j > 2

List(1,2,3) filter gt2

这编译好并按预期工作。然而,尝试撰写函数会导致隐藏错误:

List(1,2,3) filter gt2(twice(_))

  error: missing parameter type for expanded function ((x$1) => twice(x$1))
          List(1,2,3) filter gt2(twice(_))
                                     ^ 

发生什么事了?基本上,当Scala编译器看到下划线时,将它绑定到最直接的上下文中,在这种情况下为twice(_)。这意味着我们现在使用函数 gt2()作为参数调用twice。编译器知道的是该函数接受一个参数并返回相同的类型。可以说它应该根据Int签名找出这个参数的类型并且返回类型为twice(),但是它会暂时使用x$1占位符,直到他稍后再计算出来。

不幸的是它无法做到这一点,因为gt2()在我们提供函数时期望Int(至少这是编译器认为的那样)。

为什么:

List(1,2,3) filter {k => gt2(twice(k))}

工作?编译器事先不知道k的类型。但是,它知道gt2会返回Boolean并期望Int。它还知道twice()期望Int并返回一个k。这样它就可以推断出filter的类型。另一方面,编译器从一开始就知道Int => Boolean期望(_)


回到你的案子。单独的下划线(!_)不考虑“上下文”,因此编译器会搜索另一个最直接的封闭上下文。 _ == true本身就被认为是一个函数,以及(x$1) => List(true, false).filter(x$1).size 。但不是单独的下划线。

那么在这种情况下,最接近的直接背景是什么(我确定有一个科学名称......)?好吧,整个表达方式:

List(true, false).filter(x$1).size

编译器认为您正在尝试创建一个函数,该函数接受一些未知类型的参数并返回表达式类型的某些内容:filter。再次可以说,它应该能够确定Boolean => Boolean需要Int并返回.sizeList(true,false) filter (_ == true) List(true,false) filter (i => i) List(true,false) filter identity ),但显然它没有。


那你能做什么?您必须为编译器提供一个提示,即应在较小的上下文中解释下划线。你可以说:

{{1}}

答案 1 :(得分:3)

第一个错误是因为Scala不知道_的内容。所以试试这个......

List(true,false).filter(_:Boolean).size

之后,您会获得更多信息:

<console>:8: error: type mismatch;
found   : Boolean
required: (Boolean) => Boolean
 List(true,false).filter(_:Boolean).size

它只是评估_作为值而不是函数。根据ScalaDoc

filter (pred: (A) ⇒ Boolean): GenTraversable[A] 

答案 2 :(得分:2)

我看到错误信息得到了很大改善!让我们看看您写的错误消息和您的工作版本:

((x$1) => List(true, false).filter(x$1).size)
          List(true,false).filter(a => a).size

或者,调整空格,括号和变量名称:

a => List(true, false).filter(a     ).size
     List(true, false).filter(a => a).size

现在看起来一样吗?

简而言之,当您传递下划线代替参数时,您正在执行partial function application。您可能更熟悉下划线被用作匿名函数中参数的占位符,这是当它出现在表达式中时会发生的情况,如_ + 1。这两种用法是不同的,即使它们都导致匿名函数。