2010-12-18 112 views
6

你知道那場景再見,謝謝你所有的魚亞瑟如此非常高興地停下來服務員,並要求知道,「爲什麼這樣的食物很好?」我正處於這種情況。斯卡拉似乎正在做我想做的事,但我不明白它是如何做到的。考慮以下內容:如何修改無列表?

scala> var v = Nil:List[String]; 
v: List[String] = List() 

scala> v.length 
res38: Int = 0 

scala> v ::= "Hello" 

scala> v.length 
res39: Int = 1 

scala> Nil.length 
res40: Int = 0 

這正是您希望的,但它是如何發生的?

Nil是一個擴展List [Nothing]的對象,它是List [String]的子類型,因此賦值工作正常,但它是一個不可變的列表,不是嗎?所以我不應該能夠追加到它。但是我可以追加到它,或者至少我可以追加到V,我認爲它指向無。 v被改變,但是Nil不是。

那麼,WTF? Scala有一些我不知道的聰明的copy-on-modify語義嗎? Nil真的是一個返回空列表的函數嗎?更廣泛地說,有沒有什麼方法可以讓REPL回答這些問題?

回答

16

當Scala中看到

v ::= "Hello" 

它首先檢查是否v知道方法::=。在這種情況下(類型List),它沒有,所以,因爲該方法僅由符號和與=符號結束時,它會嘗試別的東西:

v = v.::("Hello") 

,順便說一下,被寫入"Hello" :: v中綴記號。現在,因爲v知道方法::,所以它工作並返回一個新的List,然後按照指示將其分配到v

順便說一下,它是預先計劃,而不是追加。

4

當您使用關鍵字var時,您不是在Java意義上「翻譯爲final」。相反,var允許重新分配。當您致電運營商::=時,您實際上正在重新分配v指向的內容。運營商::=返回一個新的列表,而不是附加了「Hello」的原始列表。因此,v現在指向「你好」,而不是Nil列表。

這裏,看這個:

var myThing = new List(1, 2, 3) 
var myOhterThing = myThing 

myThing = new List(1, 2, 3, 4) 

這幾乎等於說「採取的第一個列表中,將它複製並追加一個‘4’到它現在分配‘myThing’指向該列表。 「使用該操作符可以有效地完成相同的操作。用這種方式寫出來讓你看到。

2

無是不可變的。當你對它負責(::)你得到一個新的實例也是不可變的。

試試這個:

val v1 = Nil 
val v2 = "Hello" :: v1 
v1 == v2 

(你會得到錯誤的,因爲它們不指向同一個對象)

由於v是您可以重新分配值給它的變種。

因此,當您有:

v ::= "Hello" // what you really have is: 
v = "Hello" :: v // or 
v = "Hello" :: Nil // or 
v = List("Hello") 

上面創建一個新的列表和無保持不變。它類似於Java中的String添加。由於字符串是不可變的,你永遠不會改變一個實例 - 只能創建新的實例。

由於無沒有改變,Nil.length = 0

3

與VAL v嘗試它,看看有什麼改變的。 :)