使用“_”定义的相同函数的执行与Scala中使用命名变量定义的函数的执行方式有何不同?
以下是两个函数定义的输出。
scala> import scala.util.Random
import scala.util.Random
scala> Seq("a", "b", "c").map(x => { val rand = new Random().nextInt; x+":"+rand}).foreach(println)
a:1700883193
b:-1153799665
c:-784839760
scala> Seq("a", "b", "c").map({ val rand = new Random().nextInt; _+":"+rand}).foreach(println)
a:-1775524209
b:-1775524209
c:-1775524209
是否与部分定义的函数有关?我可能会遗漏一些东西。
似乎是重复的说明:我看到了类似的问题,但问题并未明确。注意到了不同点的答案。所以在这里问一下简单&直接的例子。另一个问题的链接: Why are values defined only once while using underscore in Scala 希望很清楚。
答案 0 :(得分:5)
这不是因为部分功能,而是closure的工作方式。当你写:
_ + ":" + rand
编译器交织:
(x: String) => x.+(":").+(rand)
这是Function1[String, String]
。编译器将lambda表达式扩展为具体的AbstractFunction1
和捕获 rand
。以下是生成的类的相关代码段:
<synthetic> <paramaccessor> private[this] val rand$1: Int = _;
def <init>(rand$1: Int): <$anon: Function1> = {
anonfun$1.this.rand$1 = rand$1;
anonfun$1.super.<init>();
()
请注意,rand$1
会被分配到生成的this.rand$1
实例中的本地字段AbstractFunction1
。每次调用map
时都会重复使用此字段,这就是为什么每次迭代都没有看到新值,而只是第一次出现。您可以在字节代码中看到init
方法仅在运行main
时调用一次:
public void main(java.lang.String[]);
Code:
0: aload_0
1: ldc #16 // String he
3: ldc #18 // String hello
5: new #20 // class Chapter5/X$$anonfun$main$1
8: dup
9: invokespecial #21 // Method Chapter5/X$$anonfun$main$1."<init>":()V <--- Here.
12: invokespecial #25 // Method f$1:(Ljava/lang/Object;Ljava/lang/Object;Lscala/Function1;)Ljava/lang/Object;
15: pop
16: return
其中f$1
是遍历集合的main
方法体,并为序列中的每个元素调用apply
。
相反,当您为集合中的每个值引入一个新变量时,如下所示:
map(x => { val rand = new Random().nextInt; x+":"+rand})
我们看到rand
不再被捕获,通过绑定到每个apply
调用的新变量:
@SerialVersionUID(value = 0) final <synthetic> class anonfun$1 extends scala.runtime.AbstractFunction1 with Serializable {
final def apply(x: String): String = {
val rand: Int = new scala.util.Random().nextInt();
x.+(":").+(scala.Int.box(rand))
};
答案 1 :(得分:2)
你似乎对这个街区感到困惑。 ….map(x => { val rand = new Random().nextInt; x+":"+rand }).…
可以改写为
val fn = x => { val rand = new Random().nextInt; x+":"+rand })
….map(fn).…
虽然….map({ val rand = new Random().nextInt; _+":"+rand }).…
与
….map({ val rand = new Random().nextInt; x => x+":"+rand }).…
可以改写为(注意x =>
)
val fn = { val rand = new Random().nextInt; x => x+":"+rand }
….map(fn).…
或(没有阻止)
val rand = new Random().nextInt
val fn = x => x+":"+rand
….map(fn).…