为什么Scala在解包元组时会构造一个新的元组?

时间:2011-11-19 07:30:54

标签: scala tuples iterable-unpacking

为什么这个Scala代码:

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

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

为构建新bar()的{​​{1}}生成以下字节码,将Tuple2Tuple2传递给它,然后从中获取值?

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 的返回值不是元组吗?

JVM是否会优化构造?

2 个答案:

答案 0 :(得分:4)

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

编译器的相关部分我们Unapplies#caseClassUnapplyReturnValue,调用TreeDSL#SOME生成代码以创建新的TupleN

答案 1 :(得分:4)

这似乎是根据spec 4.1值声明和定义 - 稍微重新格式化stackoverflow显示):

  

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

     
      
  1. 如果模式p已绑定变量x1, . . . , xn,则其中n >= 1:   在这里,$x是一个新名称。
  2.   
  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