2013-12-23 32 views
6

奇提供的功能...斯卡拉Map.getOrElse - 如何爲默認

val h = new HashMap[Long, Int]() 

    def mydefault0():Int = 101 
    println(h.getOrElse(99, default=mydefault0 _)) // Prints <function0> 

    def mydefault1(key:Long):Int = 102 
    println(h.getOrElse(98, default=mydefault1 _)) // Prints <function1> 

docs說,默認類型必須爲:=>乙

如果我理解正確,無糖在這種情況下,arg函數返回一個Int。

  1. 爲什麼示例使用mydefault1進行編譯,因爲它需要參數,所以符合規範?

  2. 爲什麼返回函數,而不是調用函數來產生默認值?顯然,類型安全已被破壞,因爲getOrElse必須返回一個Int,而不是一個函數。 (如果我誤解了文檔並錯誤地提供了需要Int值的函數,爲什麼編譯器讓我提供一個函數而不是Int?)。

編輯

很明顯:

  • 擴展HashMap和壓倒一切的默認,或
  • 使用HashMap.withDefault

也讓功能通過使用指定一個默認值。我希望能夠用執行查找時提供的函數來覆蓋默認值(即,該函數在地圖的生命週期中可能會發生變化)

這可能嗎?

回答

14

The definition of getOrElse

getOrElse[B1 >: B](key: A, default: => B1): B1 

default論點採取一個函數,其將是() => B1,但B1類型的懶惰地存取的值。這個無參數的「函數」=> B1有時也被稱爲thunk。使用它的正確方法如下:

import collection.mutable 

val h = new mutable.HashMap[Long, Int]() 

def mydefault0(): Int = 101 

println(h.getOrElse(99, default = mydefault0())) 

那麼它是什麼,你與mydefault0 _看見了什麼?顯然,返回值的類型爲B1,它必須是映射值類型Int的常見超類型和默認值的類型。默認值的類型是Function0。如果分配結果,你看到超是Any

val x = h.getOrElse(99, default = mydefault0 _) // x: Any = <function0> 

所以錯誤是假設你必須通過一個函數的時候,居然你指出這是懶惰計算的表達式。只有在需要默認值時纔會調用mydefault0()。形式上,參數被定義爲call-by-name參數。


編輯:關於你的評論。按名稱呼叫意味着你每次都得到一個新數組。

val m = Map("foo" -> Array(1, 2, 3)) 

def myDefault = { 
    println("called-default") 
    Array(4, 5, 6) 
} 

val a1 = m.getOrElse("foo", myDefault) // myDefault not called 
val a2 = m.getOrElse("bar", myDefault) // myDefault called 
val a3 = m.getOrElse("baz", myDefault) // myDefault called 
a2 == a3 // false!! 
+0

謝謝,這清除了我的錯誤。但回到問題... 因此,假設我想要一個Map [String,Array [Int]],我將無法使用getOrElse構造一個新的默認數組,當密鑰丟失時,對不對?這會導致每個值成爲相同的對象。換句話說,沒有規定提供在每次查找時生成默認值的函數(覆蓋HashMap的默認值,或者使用Map.withDefault不允許我在查找時指定默認生成器)。 – user48956

+1

正確理解名稱參數是什麼以及它們的行爲如何(以及它們爲什麼不是*「懶惰」)非常重要。瞭解名稱參數最重要的是* *調用*方法確定實際參數表達式評估的次數,可能是0次,1次或更多倍。懶惰參數僅評估0或1次。 –

+0

謝謝 - 這一切現在都有道理。 – user48956