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

发布于 2024-08-23 19:49:56 字数 484 浏览 2 评论 0原文

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

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)

Why do i get an error when I try using _ instead of using a named identifier?

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)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

蓝天白云 2024-08-30 19:49:56

像这样用来代替变量名的下划线是特殊的;第 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 })

简而言之,在函数的参数列表中使用下划线意味着“我忽略这些参数”在此函数的主体中。”在正文中使用它们意味着“编译器,请为我生成一个参数列表”。这两种用法不能很好地混合。

Underscores used in place of variable names like that are special; the Nth underscore means the Nth argument to an anonymous function. So the following are equivalent:

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

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

But, if you do this:

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

Then you are mapping the list with a function that ignores its single argument and returns the function defined by _ + 1. (This specific example won't compile because the compiler can't infer what type the second underscore has.) An equivalent example with named parameters would look like:

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

In short, using underscores in a function's argument list means "I am ignoring these arguments in the body of this function." Using them in the body means "Compiler, please generate an argument list for me." The two usages don't mix very well.

清音悠歌 2024-08-30 19:49:56

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

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))
       _ + _
           ^

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

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

To complement the other answers, here are some examples showing why you get the "missing parameter type" in some cases when using '_' as a placeholder parameter.

Scala's type inference considers the 'expected' type of an expression based on its context. If there is no context, it cannot infer the type of the parameters. Notice in the error message the first and second instances of _ are replaced with the compiler generated identifiers x$1 and x$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))
       _ + _
           ^

Adding a type ascription to the entire expression provides enough context to help the inferencer:

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

Alternatively, you can add a type ascription to each parameter placeholder:

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

In the function call below with type arguments provided, the context is unambigous and the function type is inferred.

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

However, if we ask the compiler to infer the type parameters, if fails:

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, _ + _)
                     ^

We can help it, though, by currying the parameter lists. Here, the arguments to the first parameter list (1, 1), tell the inference that the type parameter A should be Int. It then knows that the type of the argument f must be (Int, Int) => ?), and the return type R is inferred as Int, the result of integer addition. You will see the same approach used in Traversable.flatMap in the standard library.

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
×纯※雪 2024-08-30 19:49:56

如果您不打算绑定标识符,请忽略该部分。

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

If you're not going to bind an identifier, just leave that part out.

res0.map("item "+_.toString)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文