2011-11-19 21 views
8

¿Por qué este código Scala:¿Por qué Scala construye un nuevo Tuple al desempaquetar un Tuple?

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

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

generar el siguiente código de bytes para bar() que construye un nuevo Tuple2, pasa el Tuple2 de foo() a él y luego obtiene los valores fuera de él?

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 

Es esto porque el compilador no está comprobando que foo() s valor de retorno no es un par de valores?

¿La JVM optimizará la construcción de todos modos?

+0

el valor de retorno de 'foo' es una tupla. ¿Por qué crees que no es así? –

+0

foo devuelve un tipo, pero en 9: en barra() construye una nueva tupla. –

Respuesta

4

Esto parece ser según los spec (en 4.1 Valor declaraciones y definiciones - ligeramente reformateado para stackoverflow pantalla):

Las definiciones del valor pueden tener alternativamente un patrón (§8.1) como lado izquierdo. Si p es algún patrón que no sea un simple nombre o un nombre, seguido de dos puntos y un tipo, entonces la definición de valor val p = e se expande como sigue:

variables de
  1. Si el patrón p se ha unido x1, . . . , xn, donde n >= 1: Aquí, $x es un nombre nuevo.
val $x = e match {case p => (x1, . . . , xn)} 
    val x1 = $x._1 
    . . . 
    val xn = $x._n 

lo tanto la creación tupla ocurre en la fase analizador. Así val (i, s) = (1, "s") gasta al final de la fase de analizador para:

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 

Medición de esto en esta simple prueba en un millón de iteraciones:

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 } 

rendimientos

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

y con la bandera -Optimizar :

foo: Elapsed: 0.027 
bar: Elapsed: 0.049 
._1 ._2 access: Elapsed: 0.029 
+0

Gracias. Por cierto, el compilador de Scala 2.9 con y sin -optimize genera el mismo bytecode para la clase de prueba que pegué, pero tal vez ayudó en el código de evaluación comparativa para hacer que los tiempos sean más rápidos. –