如何使用"_"定义的相同函数的执行与使用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
Run Code Online (Sandbox Code Playgroud)
它是否与部分定义的函数有关?我可能会遗漏一些东西.
似乎是重复的解释:我看到了类似的问题,但重点并不明确,并注意到不同点的答案.所以在这里问一下简单直接的例子.另一个问题的链接: 为什么在Scala中使用下划线时只定义一次值 希望它很清楚.
这不是因为部分功能,而是闭包的工作方式.当你写:
_ + ":" + rand
Run Code Online (Sandbox Code Playgroud)
编译器互相配合:
(x: String) => x.+(":").+(rand)
Run Code Online (Sandbox Code Playgroud)
这是一个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>();
()
Run Code Online (Sandbox Code Playgroud)
请注意,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
Run Code Online (Sandbox Code Playgroud)
遍历集合f$1的main方法体在哪里,并调用apply序列中的每个元素.
相反,当您为集合中的每个值引入一个新变量时,如下所示:
map(x => { val rand = new Random().nextInt; x+":"+rand})
Run Code Online (Sandbox Code Playgroud)
我们看到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))
};
Run Code Online (Sandbox Code Playgroud)