如何使用"_"定义的相同函数的执行与使用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
它是否与部分定义的函数有关?我可能会遗漏一些东西.
似乎是重复的解释:我看到了类似的问题,但重点并不明确,并注意到不同点的答案.所以在这里问一下简单直接的例子.另一个问题的链接: 为什么在Scala中使用下划线时只定义一次值 希望它很清楚.
这不是因为部分功能,而是闭包的工作方式.当你写:
_ + ":" + rand
编译器互相配合:
(x: String) => x.+(":").+(rand)
这是一个Function1[String, String]
.编译器将lambda表达式扩展为具体AbstractFunction1
和捕获 rand
.以下是生成的类的相关片段:
private[this] val rand$1: Int = _; def (rand$1: Int): <$anon: Function1> = { anonfun$1.this.rand$1 = rand$1; anonfun$1.super. (); ()
请注意,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."":()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) finalclass 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)) };