2010-12-01 70 views
9

我有一個三級的數據結構(縮進和換行的可讀性):如何訪問和地圖地圖的可變地圖更新值

scala> import scala.collection.mutable.Map 
import scala.collection.mutable.Map 

scala> val m = Map("normal" -> Map("home" -> Map("wins" -> 0, "scores" -> 0), 
            "away" -> Map("wins" -> 0, "scores" -> 0))) 
m: scala.collection.mutable.Map[java.lang.String, 
    scala.collection.mutable.Map[java.lang.String, 
    scala.collection.mutable.Map[java.lang.String,Int]]] = 
Map((normal,Map(away -> Map(wins -> 0, scores -> 0), 
    home -> Map(wins -> 0, scores -> 0)))) 

訪問最裏面的數據(得分)需要大量的打字:

import org.scalatest.{Assertions, FunSuite} 

class MapExamplesSO extends FunSuite with Assertions { 
    test("Update values in a mutable map of map of maps") { 
    import scala.collection.mutable.Map 
    // The m map is essentially an accumulator 
    val m = Map("normal" -> 
       Map("home" -> Map("wins" -> 0, "scores" -> 0), 
        "away" -> Map("wins" -> 0, "scores" -> 0) 
       ) 
     ) 
    // 
    // Is there a less verbose way to increment the scores ? 
    // 
    assert(m("normal").apply("home").apply("scores") === 0) 

    val s1 = m("normal").apply("home").apply("scores") + 1 
    m("normal").apply("home").update("scores", s1) 

    assert(m("normal").apply("home").apply("scores") === 1) 

    val s2 = m("normal").apply("home").apply("scores") + 2 
    m("normal").apply("home").update("scores", s2) 

    assert(m("normal").apply("home").apply("scores") === 3) 
    } 
} 

是否有修改分數的值更簡潔的方式?

我是Scala的新手,所以對上面代碼的所有其他觀察也是受歡迎的。

回答

21

你不必使用 「應用」 只是做它通常與 「()」

m("normal")("home")("scores") = 1 
+1

在這個例子中有效,但如果m(「normal」)(「home」)還沒有被定義,它將不起作用。 – Mark 2016-10-12 00:46:48

10

你可以寫

m("normal").apply("home").apply("scores") 

m("normal")("home")("scores") 

不過我我不確定這樣的結構是不是一個好主意。也許你應該考慮把這個功能封裝在一個專門的類中。

3

添加本地助手功能始終是減少代碼重複好辦法:

class MapExamplesSO { 
    def test { 
    import scala.collection.mutable.Map 
    // The m map is essentially an accumulator 
    var m = Map("normal" -> 
       Map("home" -> Map("wins" -> 0, "scores" -> 0), 
        "away" -> Map("wins" -> 0, "scores" -> 0))) 


    //Local Helper returns (Old, New) 
    def updateScore(k1 : String,k2 : String,k3 : String) 
        (f : Int => Int) : (Int, Int) = { 
     val old = m(k1)(k2)(k3) 
     m(k1)(k2)(k3) = f(old) 
     (old, m(k1)(k2)(k3)) 
    } 

    assert(m("normal")(home")("scores") === 0) 
    assert(updateScore("normal","home","scores")(_+1)._2 === 1) 
    assert(updateScore("normal","home","scores")(_+2)._2 === 3) 
    } 
} 

[編輯製作代碼更緊]

+0

聰明! (我只是希望不要太聰明:)在我意識到這裏發生了什麼之前,給了我幾個讀數。很好的答案,謝謝! – user272735 2010-12-01 20:01:52

3

更簡潔:

assert(m("normal")("home")("scores") === 0) 

val s1 = m("normal")("home")("scores") + 1 
m("normal")("home")("scores") = s1 

assert(m("normal")("home")("scores") === 1) 

val s2 = m("normal")("home")("scores") + 2 
m("normal")("home")("scores") = s2 

assert(m("normal")("home")("scores") === 3) 

這需要的優勢事實上,如上所見,applyupdate都具有它們的語法糖。時間更短:

// On REPL, put both these definitions inside an object instead 
// of entering them on different lines 
def scores = m("normal")("home")("scores") 
def scores_=(n: Int) = m("normal")("home")("scores") = n 

assert(scores === 0) 

val s1 = scores + 1 
scores = s1 

assert(scores === 1) 

val s2 = scores + 2 
scores = s2 

// Just so you see these updates are being made to the map: 
assert(m("normal")("home")("scores") === 3) 

這需要對getter和setter 的語法糖(吸氣定義必須存在的setter定義工作)的優勢。

+0

這也是一個非常有用的答案 - 謝謝!我希望你也有許多讚揚。爲初學者解釋Getter/setter:http://www.dustinmartin.net/2009/10/getters-and-setters-in-scala/ – user272735 2010-12-02 07:15:12