8

为什么这个 Scala 代码:

class Test
{
  def foo: (Int, String) =
  {
    (123, "123")
  }

  def bar: Unit =
  {
    val (i, s) = foo
  }
}

bar()为构造一个 new生成以下字节码Tuple2,将Tuple2from传递foo()给它,然后从中获取值?

public void bar();
Code:
 0:   aload_0
 1:   invokevirtual   #28; //Method foo:()Lscala/Tuple2;
 4:   astore_2
 5:   aload_2
 6:   ifnull  40
 9:   new     #7; //class scala/Tuple2
 12:  dup
 13:  aload_2
 14:  invokevirtual   #32; //Method scala/Tuple2._1:()Ljava/lang/Object;
 17:  aload_2
 18:  invokevirtual   #35; //Method scala/Tuple2._2:()Ljava/lang/Object;
 21:  invokespecial   #20; //Method scala/Tuple2."<init>":(Ljava/lang/Object;Ljava/lang/Object;)V
 24:  astore_1
 25:  aload_1
 26:  invokevirtual   #39; //Method scala/Tuple2._1$mcI$sp:()I
 29:  istore_3
 30:  aload_1
 31:  invokevirtual   #35; //Method scala/Tuple2._2:()Ljava/lang/Object;
 34:  checkcast       #41; //class java/lang/String
 37:  astore  4

这是因为编译器没有检查foo()s 返回值不是元组吗?

JVM 会优化构造吗?

4

2 回答 2

4

这似乎是 scalac 中错过的优化机会。

编译器的相关部分是Unapplies#caseClassUnapplyReturnValue,它调用TreeDSL#SOME生成代码来创建一个新的TupleN

于 2011-11-19T08:14:38.850 回答
4

这似乎符合规范(在4.1 值声明和定义中- 为 stackoverflow 显示略微重新格式化):

值定义也可以有一个模式(第 8.1 节)作为左侧。如果 p 不是简单名称或名称后跟冒号和类型之外的某种模式,则值定义val p = e扩展如下:

  1. 如果模式p具有绑定变量x1, . . . , xn,其中n >= 1: 这里$x是一个新名称。
  val $x = e match {case p => (x1, . . . , xn)}
  val x1 = $x._1
  . . .
  val xn = $x._n

所以元组的创建发生在解析器阶段。所以val (i, s) = (1, "s")在解析器阶段结束时花费:

private[this] val x$1 = scala.Tuple2(1, "s"): @scala.unchecked match {    
  case scala.Tuple2((i @ _), (s @ _)) => scala.Tuple2(i, s)
};
val i = x$1._1;
val s = x$1._2

在这个简单的一百万次迭代测试中测量这一点:

def foo: (Int, String) = (123, "123")
def bar: Unit = { val (i, s) = foo }
def bam: Unit = { val f = foo; val i = f._1; val s = f._2 }

产量

foo: Elapsed: 0.030
bar: Elapsed: 0.051
._1 ._2 access: Elapsed: 0.040

并带有 -optimize 标志:

foo: Elapsed: 0.027
bar: Elapsed: 0.049
._1 ._2 access: Elapsed: 0.029
于 2011-11-19T17:05:41.333 回答