web-dev-qa-db-ja.com

Scalaマップの要素に番号を追加する(キーが存在する場合)か、新しい要素を挿入しない)ための優れた方法

私はいくつかの同様の質問について知っています。それらは私を助けません-既存のキーがない場合、コードは機能しません。

既存のキーに追加する値(存在する場合)または新しいキーとして配置する(マップに適切なキーが含まれていない場合)値をマップに追加するには、いくつかの優れたアプローチが必要です。

次のコードは機能しますが、私はそれが好きではありません:

_val a = collection.mutable.Map(("k1" -> 1), ("k2" -> 5))
val key = "k1"

val elem = a.get(key)
if (elem == None) {
    a += ("k5" -> 200)
} else {
    a.update(key, elem.get + 5)
}
_

より良いものへのポイントはありますか?現在のScalaバージョンは2.10.4であり、現在2.11に切り替えることはできません。可変マップは100%の制限ではありませんが、推奨されます。

たとえば、これは 同様の質問 ですが、そこに記載されていない存在しないキーの場合も考慮する必要があります。少なくとも、a.get(key)Noneである可能性があることを理解するか、より良いアプローチを追加する必要があります。良いアイデアは_|+|_でしたが、基本的なScala 2.10.xを維持したいと思います。

16

その目的のために独自の関数を作成できます。

def addOrUpdate[K, V](m: collection.mutable.Map[K, V], k: K, kv: (K, V), 
                      f: V => V) {
  m.get(k) match {
    case Some(e) => m.update(k, f(e))
    case None    => m += kv
  }
}

addOrUpdate(a, "k1", "k5" -> 200, (v: Int) => v + 5)
11
Jean Logeart

それを行うための最短の方法:

a += a.get(key).map(x => key -> (x + 5)).getOrElse("k5" -> 200)

一般に:

a += a.get(k).map(f).map(k -> _).getOrElse(kv)

辞書が不変の場合も同じです。

m + m.get(k).map(f).map(k -> _).getOrElse(kv)

したがって、ここで可変コレクションを使用する理由はわかりません。

これらすべてが気に入らない場合はOption.mapもの:

m + (if (m.contains(k)) k -> f(m(k)) else kv)

可能なバリエーションのクラス全体があることに注意してください。

k1 -> f(m(k1)) else k2 -> v2 //original
k1 -> f(m(k1)) else k1 -> v2
k1 -> f(m(k2)) else k2 -> v2
k1 -> f(m(k2)) else k1 -> v2
k2 -> v2 else k1 -> f(m(k1)) 
k1 -> v2 else k1 -> f(m(k1))
k2 -> v2 else k1 -> f(m(k2))
k1 -> v2 else k1 -> f(m(k2))
... //v2 may also be a function from some key's value

では、なぜそれが標準機能ではないのですか? IMO。すべてのバリエーションをワンライナーとして実装できるためです。ワンライナーとして実装できるすべての関数を備えたライブラリが必要な場合は、Scalazです:)。

P.S.なぜ「update(d)ifpersist」関数がないのか疑問に思う場合-@ Rex Kerrの回答を参照してください ここ

14
dk14

これを行うための1つのやや明確な方法:

val a = collection.mutable.Map[String, Int]() withDefault insertNewValue

def insertNewValue(key: String): Int =
  a += key -> getValueForKey(key)
  a(key)
}

def getValueForKey(key: String): Int = key.length

それでも、可変コレクションの使用は完全に推奨しません。不変フィールドを保持する変数として、内部の可変状態を保持することが望ましいです。

これは単純なルールのためです。完全に必要な場合を除いて、内部状態を公開しないでください。公開する場合は、発生する可能性のある潜在的な副作用を可能な限り減らす必要があります。

可変状態の参照を公開すると、他のアクターがその値を変更できるため、参照の透過性が失われます。 可変コレクションへのすべての参照が非常に長く、使用が難しいのは偶然ではありません。それはあなたへの開発者のメッセージです

当然のことながら、コードは同じままです、マップのインスタンス化でいくつかの最小限の変更があります。

var a = Map[String, Int]() withDefault insertNewValue

def insertNewValue(key: String): Int = {
  a += key -> getValueForKey(key)
  a(key)
}

def getValueForKey(key: String): Int = key.length 
1