在Scala中,使用`_`和使用命名标识符有什么区别?

Sco*_*bie 10 scala wildcard

当我尝试使用_而不是使用命名标识符时,为什么会出现错误?

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)
Run Code Online (Sandbox Code Playgroud)

Dav*_*low 18

用于代替变量名称的下划线是特殊的; 第N个下划线表示匿名函数的第N个参数.所以以下是等价的:

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

List(1, 2, 3).map(_ + 1)
Run Code Online (Sandbox Code Playgroud)

但是,如果你这样做:

List(1, 2, 3).map(_ => _ + 1) 
Run Code Online (Sandbox Code Playgroud)

然后,您将使用忽略其单个参数的函数映射列表,并返回由其定义的函数_ + 1.(此特定示例将无法编译,因为编译器无法推断第二个下划线具有哪种类型.)具有命名参数的等效示例如下所示:

List(1, 2, 3).map(x => { y => y + 1 })
Run Code Online (Sandbox Code Playgroud)

简而言之,在函数的参数列表中使用下划线意味着"我在这个函数的主体中忽略了这些参数." 在体内使用它们意味着"编译器,请为我生成一个参数列表." 这两种用法并不是很好.


ret*_*nym 5

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

Scala 的类型推断根据其上下文考虑表达式的“预期”类型。如果没有上下文,则无法推断参数的类型。请注意,在错误消息中,第一个和第二个实例 的_替换为编译器生成的标识符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))
       _ + _
           ^
Run Code Online (Sandbox Code Playgroud)

向整个表达式添加类型归属提供了足够的上下文来帮助推理:

scala> (_ + _) : ((Int, Int) => Int)
res3: (Int, Int) => Int = <function2>
Run Code Online (Sandbox Code Playgroud)

或者,您可以为每个参数占位符添加类型归属:

scala> (_: Int) + (_: Int)          
res4: (Int, Int) => Int = <function2>
Run Code Online (Sandbox Code Playgroud)

在下面提供类型参数的函数调用中,上下文是明确的,函数类型是推断出来的。

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
Run Code Online (Sandbox Code Playgroud)

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

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, _ + _)
                     ^
Run Code Online (Sandbox Code Playgroud)

不过,我们可以通过柯里化参数列表来帮助它。在这里,第一个参数列表的参数(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
Run Code Online (Sandbox Code Playgroud)