为什么这不起作用:
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。
答案 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
并返回.size
(List(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
。这两种用法是不同的,即使它们都导致匿名函数。