2012-04-13 18 views
19

私の現在のユースケースは非常に些細なので、変更可能または不変のいずれかのマップがそのトリックを行います。変更可能なスカラ不変マップ、いつ変更可能ですか?

はその後、同様

def doFoo(foo: String = "default", params: Map[String, Any] = Map()) { 

    val newMap = 
    if(someCondition) params + ("foo" -> foo) else params 

    api.doSomething(newMap) 
} 

問題の地図は、一般的にあるかもしれませんほとんどで、非常に小さいであろう不変の地図を取り、サードパーティのAPIメソッドを呼び出し不変の地図を取る方法を、持っていますケースクラスインスタンスの埋め込みリスト、数千のエントリまでしたがって、この場合も、不変になることにほとんど影響がないと仮定します(つまり、newMap valコピーを介して本質的に2つのインスタンスを持つ)。

それでも、マップをコピーして、いくつかのk-> vエントリが追加された新しいマップを取得するだけです。

私はタックしたい項目にparams.put("bar", bar)などをつけてから、params.toMapに変えてapi呼び出しのために不変にすることができます。これはオプションです。変更可能なマップをインポートして渡す必要があります。これは、Scalaのデフォルトの不変のマップに比べると面倒です。

変更可能なマップを不変のマップに使用することが正当化されたか良い習慣であるかについての一般的なガイドラインは何ですか?

おかげ

EDIT ので、不変のマップ上の追加操作がDHGのとのために問題を解決し、完全なコピーが作成されていないことを@ニコラスの主張、@を確認し、一定の時間近くかかることが表示されます具体的なケースが提示された。

+4

[極端な賢さ:Scalaの機能データ構造](http://www.infoq.com/presentations/Functional-Data-Structures-in-Scala)を参照してください。 –

+0

ありがとう、今それを見て – virtualeyes

答えて

29

不変のマップの実装によっては、いくつかのエントリを追加しても元のマップ全体が実際にコピーされないことがあります。これは、不変のデータ構造アプローチの利点の1つです。Scalaは、できるだけコピーを取り除こうとします。

この種の動作は、Listで最も簡単です。 val a = List(1,2,3)があれば、そのリストはメモリに保存されます。しかし、val b = 0 :: aのような追加要素を追加すると、となり、新しい4要素Listが返されますが、ではなく、aがコピーされます。代わりに、bという新しいリンクを1つ作成し、既存のリストaへのポインタを渡しました。

他の種類のコレクションでもこのような戦略を構想することができます。たとえば、Mapに1つの要素を追加すると、コレクションは既存のマップをラップして、必要に応じて元のマップに戻し、APIを1つのMapのように提供することができます。

+1

+1、クール、非問題は、その場合です。いくつかのk-> vをオリジナルマップ – virtualeyes

+0

に追加する必要があるときに完全なコピーを作成するという考えにはちょうど迷惑だった。不変なコレクションが追加によって堅牢で、そうでないものは説明が面白いだろう。私はListMapsが非常に堅牢ではないだろうと期待しています。 –

5

dhgの回答に加えて、performance of the scala collectionsをご覧ください。追加/削除操作が線形時間を要しない場合は、構造全体を単にコピーする以外に何かを行わなければなりません。 (逆のことは真実ではないことに注意してください:構造全体をコピーするには線形時間が必要ではありません)

+0

+1、すばらしいリンク、ありがとう。パフォーマンスと特性によると、不変のHashMapの追加操作はほぼ一定の時間がかかりますが、現時点では聞きたいものです;-) – virtualeyes

14

可変オブジェクトを使用すること自体が悪くはありません。機能プログラミング環境では、関数を純粋に保つことによって副作用を避け、オブジェクトを不変にします。

ただし、関数内に可変オブジェクトを作成してこのオブジェクトを変更すると、関数外でこのオブジェクトへの参照を解放しない限り、関数は純粋です。今、私はこのアプローチは2つの場合にお勧め/便利だと思います

def buildVector(x: Double, y: Double, z: Double): Vector[Double] = { 
    val ary = Array.ofDim[Double](3) 
    ary(0) = x 
    ary(1) = y 
    ary(2) = z 
    ary.toVector 
} 

:のようなコードを持つことが可能です(1)性能、不変オブジェクトを作成および変更することはあなたの全体のアプリケーションのボトルネックになっている場合。 (レンズやジッパーなどに頼るのではなく)複雑なオブジェクトを修正する方が簡単な場合があるので、コードの可読性がある場合があります。

+0

はい、不変のデフォルトを維持する代わりに、変更可能なマップを渡していましたapi呼び出しを行う前に不変のマップに変換するので、局所的なアプローチではなく機能的に純粋ではありません。それは一般的な、非極端な場合のように聞こえる、不変のデフォルトは正常です。もちろん、私は不変の上に変更可能なマップの具体的な使用例についてはまだ不明です。私に質問をさせた簡単なケースを提供しないことでドアを開いたままにしておく必要があります。 – virtualeyes

+0

+1:これは基本的に、MicrosoftのBCL不変コレクション(https://www.nuget。 org/packages/Microsoft.Bcl.Immutable)を参照してください。 –

0

私は、宣言されたパラメータ型(入力値または戻り値)不変のマップではなく、変更可能です。コレクションマップは、両方のタイプの実装で機能する不変のインターフェイスです。マップを使用するコンシューマメソッドは、実際にマップ実装やその構築方法について知る必要はありません。 (とにかくそのビジネスは本当にありません)。

マップの特定の構成(変更可能または不変)を使用しているコンシューマから地図の特定の構成を隠すアプローチを使用すると、依然として本質的に不変のマップダウンストリームが得られます。また、不変インターフェースとしてcollection.Mapを使用することで、immutable.Map型オブジェクトを使用するように作成されたコンシューマーで行うすべての ".toMap"非効率性を完全に除去します。完全に構築されたマップを別のものに変換するだけで、最初のものでサポートされていないインターフェイスに準拠することは、本当に不要なオーバーヘッドになります。

ここから、3つの別々のインターフェイスセット(可変マップ、不変マップ、コレクションマップ)を振り返り、時間の99%が本当に必要なものであることを認識します(変更可能(不幸にも)デフォルトの不変のマップインターフェイスを使用すると、実際には「スケーラブル言語」に多くの不必要なオーバーヘッドが追加されることになります。

関連する問題