2010-03-01 40 views
9

为什么我尝试使用_而不是使用指定的标识符时出现错误?在Scala中,使用`_`和使用命名标识符有什么区别?

scala> res0 
res25: List[Int] = List(1, 2, 3, 4, 5) 

scala> res0.map(_=>"item "+_.toString) 
<console>:6: error: missing parameter type for expanded function ((x$2) => "item 
".$plus(x$2.toString)) 
     res0.map(_=>"item "+_.toString) 
         ^

scala> res0.map(i=>"item "+i.toString) 
res29: List[java.lang.String] = List(item 1, item 2, item 3, item 4, item 5) 

回答

18

下划线用来代替这样的变量名是特殊的;第N个下划线表示匿名函数的第N个参数。所以下面的是等价的:

List(1, 2, 3).map(x => x + 1) 

List(1, 2, 3).map(_ + 1) 

但是,如果你这样做:

List(1, 2, 3).map(_ => _ + 1) 

然后你映射与忽略其单个参数,返回由_ + 1定义功能的功能列表。 (这个具体的例子不能编译,因为编译器不能推断出第二个下划线有什么类型的。)与命名参数是一个例子看起来像:

List(1, 2, 3).map(x => { y => y + 1 }) 

总之,在一个函数的参数列表使用下划线意思是“我忽略了这个函数体内的这些论点。”在主体中使用它们意味着“编译器,请为我生成一个参数列表”。两种用法混合不好。

+1

@Scoobie为了强化这一点,下划线用于Scala中许多_different_目的。正如David解释的那样,在你的例子中每个用途实际上都有不同的含义。还有其他一些含义 - 在Scala中强调,是运算符超载导致问题的一个很好的例子。虽然起初我有问题,但我可以诚实地说,我从来没有想过通过某种方式来改进它。 – 2010-03-01 01:38:41

3

如果你不打算绑定一个标识符,就把这个部分留下。

res0.map("item "+_.toString) 
4

为了补充其他答案,下面是一些例子,说明在使用'_'作为占位符参数时,在某些情况下为什么会出现“缺少参数类型”。

Scala的类型推断根据上下文考虑表达式的'expected'类型。如果没有上下文,则不能推断参数的类型。在错误消息中注意_的第一个和第二个实例被编译器生成的标识符x$1x$2所取代。

scala> _ + _ 
<console>:5: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$plus(x$2)) 
     _ + _ 
    ^
<console>:5: error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.$plus(x$2)) 
     _ + _ 
     ^

添加类型归属到整个表达式提供足够的上下文来帮助inferencer:

scala> (_ + _) : ((Int, Int) => Int) 
res3: (Int, Int) => Int = <function2> 

或者,你可以添加一个类型归属到每个参数占位符:

scala> (_: Int) + (_: Int)   
res4: (Int, Int) => Int = <function2> 

在下面的函数调用中提供了类型参数,上下文是不可信的,并且函数类型被推断出来。

scala> def bar[A, R](a1: A, a2: A, f: (A, A) => R) = f(a1, a2) 
bar: [A,R](a1: A,a2: A,f: (A, A) => R)R 

scala> bar[Int, Int](1, 1, _ + _) 
res5: Int = 2 

但是,如果我们要求编译器推断类型的参数,如果失败:

scala> bar(1, 1, _ + _)   
<console>:7: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$plus(x$2)) 
     bar(1, 1, _ + _) 
       ^
<console>:7: error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.$plus(x$2)) 
     bar(1, 1, _ + _) 
        ^

我们能帮助它,但是,通过讨好的参数列表。这里,第一个参数列表(1, 1)的参数告诉推论,类型参数A应该是Int。然后它知道参数f的类型必须是(Int, Int) => ?),并且返回类型R被推断为Int,即整数加法的结果。您会在标准库中看到Traversable.flatMap中使用的相同方法。

scala> def foo[A, R](a1: A, a2: A)(f: (A, A) => R) = f(a1, a2) 
foo: [A,R](a1: A,a2: A)(f: (A, A) => R)R 

scala> foo[Int, Int](1, 1) { _ + _ } 
res1: Int = 2 

scala> foo(1, 1) { _ + _ } 
res0: Int = 2 
相关问题