2011-11-19 64 views
8

爲什麼這個Scala代碼:爲什麼Scala在拆包Tuple時構造一個新的Tuple?

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

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

產生bar()是構建一個新的Tuple2以下的字節碼,通過從foo()給它的Tuple2,然後獲取值出來的嗎?

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()返回值是不是一個元組?

無論如何,JVM會優化建築嗎?

+0

'foo'的返回值_is_是一個元組。你爲什麼認爲它不是? –

+0

foo確實返回了一個類型,但是在9:in bar()中它構造了一個新的元組。 –

回答

4

這似乎的相關部分將根據spec(在4.1超值聲明和定義 - 稍微重新格式化爲計算器顯示):

值的定義也可以有一個模式(§8.1)作爲左邊。

  1. 如果圖案p已結合變量x1, . . . , xn,其中n >= 1: 如果p是比簡單的名稱或名稱後面跟着冒號和類型的其他一些圖案,則該值定義val p = e如下膨脹在這裏,$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 
+0

謝謝。順便說一句,帶和不帶-optimize的Scala 2.9編譯器爲我粘貼的Test類生成相同的字節碼,但也許它幫助了基準測試代碼,使時間更快。 –