如何用“_”定义的相同函数的执行与在Scala

时间:2017-01-16 14:11:10

标签: scala

使用“_”定义的相同函数的执行与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 希望很清楚。

2 个答案:

答案 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).…