2013-01-08 63 views
3

考慮以下幾點:Scala的數據結構:操作(如mapValues,過濾器...)和中間結果的鏈

val stuff = Map[String, Int]("apple" -> 5, "orange" -> 1, "banana" -> 3, "kiwi" -> 2) 

val used = 1 

val rest = stuff.mapValues{ 
    case quantity => quantity - used 
}.filterNot{ 
    case (fruit, quantity) => quantity == 0 
} 

結果是

rest : scala.collection.immutable.Map[String,Int] = Map(apple -> 4, banana -> 2, kiwi -> 1) 

雖然我不是我知道語言不是懶惰的(與Haskell不同),所以mapValues將產生一箇中間Map,這反過來將作爲輸入傳遞給filterNot(所以如果鏈中有其他操作的話)。

如何避免這種無用的中間數據結構?

注:我明白這個問題可以概括爲其他數據結構。這裏我使用Map只是因爲它是我在真實代碼中使用的數據結構(儘管有其他數據:))

+1

我不認爲它沒用;顯然它有用處。你可以在這種情況下找到'collect'方法。它將'map'和'filter'結合起來使用部分函數(PF不會處理的情況被濾除),這在源代碼和內部數據結構中都更加簡潔。 –

+0

@RandallSchulz我想過'collect',但我沒有弄清楚如何在我的例子中使用它。您能否將您的評論轉換爲答案並告訴我如何?謝謝! – MarcoS

回答

3

這似乎這樣的伎倆:

object ChainOpsRS 
{ 
    val stuff = Map[String, Int]("apple" -> 5, "orange" -> 1, "banana" -> 3, "kiwi" -> 2) 

    val used = 1 

    val rest = 
    stuff.collect { 
     case (fruit, quantity) if quantity > used => (fruit, quantity - used) 
    } 

    def main(args: Array[String]) { 
    printf("stuff=%s%n", stuff.mkString("{", ", ", "}")) 
    printf(" rest=%s%n", rest.mkString("{", ", ", "}")) 
    } 
} 

當運行產生這樣的輸出:

stuff={apple -> 5, orange -> 1, banana -> 3, kiwi -> 2} 
rest={apple -> 4, banana -> 2, kiwi -> 1} 
3

除了@金的回答,應該指出的是,mapValues方法是確實計算的中間結果:mapValues返回地圖的視圖。這使其不同於大多數其他方法,包括filterNot甚至map

一個例子:

val rest = stuff.mapValues { 
    case quantity => 
    println("reading quantity " + quantity) 
    quantity - used 
} 

rest("apple") 
rest("apple") 

打印:

reading quantity 5 
reading quantity 5 
+1

有趣!我沒有注意到'mapValues'已經返回一個'view' ......這也有點令人困惑......爲什麼'mapValues'的行爲不同? – MarcoS