在Scala的map/foreach中使用下划线

3

您能帮我理解下面第二种情况中下划线的作用吗?我猜它为列表中的每个元素定义了一个匿名函数,但为什么这个函数没有像第一种情况中那样被调用?

scala> List(1,2,3,4).foreach(x => println("*" * x))
*
**
***
****

scala> List(1,2,3,4).foreach(_ => println("*" * _))
$line25.$read$$iw$$iw$$iw$$iw$$$Lambda$1197/562203102@a632ae0
$line25.$read$$iw$$iw$$iw$$iw$$$Lambda$1197/562203102@a632ae0
$line25.$read$$iw$$iw$$iw$$iw$$$Lambda$1197/562203102@a632ae0
$line25.$read$$iw$$iw$$iw$$iw$$$Lambda$1197/562203102@a632ae0
1个回答

9
以下是正确的方法:
List(1,2,3,4).map("*" * _).foreach(println)

在Scala中,下划线有很多不同的用途。我在此列出了三个与本问题相关的用例。

用例1:在输入参数中使用下划线

当lambda表达式的输入参数在lambda表达式的主体中不会被使用时,您可以将下划线用作占位符,而不是声明lambda表达式的输入参数,如下所示。 List(1,2,3,4).foreach(_ => println("*" * 10)) // 这里显示10个星号字符,而不管输入值如何。

用例2:在lambda表达式的主体中使用下划线。

当在lambda表达式的主体中使用下划线时,它表示输入参数。如果输入仅被引用一次,则可以以这种方式使用下划线。

例如:List(1,2,3,4).foreach(println("*" * _)) // 下划线将被替换为输入参数。

用例3:引用未应用的方法。

假设我有一个方法foo(bar: Int)。我可以通过表达式foo _(即紧随其后的下划线)引用未应用的方法。 未应用函数意味着获取对可以随后按需执行的函数对象的引用。

@ def foo(bar: Int) = bar
defined function foo
@ val baz = foo _
baz: Int => Int = $sess.cmd24$$$Lambda$2592/612249759@73fbe2ce
@ baz.apply(10)
res25: Int = 10
你不能混合使用情况1和情况2。也就是说,你可以在输入参数中或者lambda函数体中使用下划线,但是不能同时使用。由于你混合了两种情况,因此会意外地使用下划线用法的情况3,如下所示:即你正在引用通过隐式定义在java.lang.String上的未应用方法*
@ "*" * _
res20: Int => String = $sess.cmd20$$$Lambda$2581/1546372166@20967474

所以,你实际上正在做以下类似的事情。
List(1,2,3,4).foreach(x => println(("*" * _).toString))

这里实际上与第三种情况无关。问题确切地说是你可以混合使用第一种和第二种情况,它们只是独立适用。 - Alexey Romanov
如果你使用 "*".* _,那就是第三种情况。但奇怪的是,这不会编译,而 "*".+ _ 可以... - Alexey Romanov
是的,"*".* _ 是第三种情况,就像我在上一行中展示的那样,它被表示为 "*" * _ - rogue-one
不,要使"*" * _成为情况3,"*" *必须是一个有效的SimpleExpr1,但它本身不是(而"*".*是)。请参见http://www.scala-lang.org/files/archive/spec/2.12/06-expressions.html#method-values。 - Alexey Romanov
所以,实际上我正在做的是针对每个元素(使用下划线作为输入参数,如情况1),打印对方法*在java.lang.String中“获得自”(不确定正确术语)的函数的引用(情况3)?谢谢! - allstar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接