28

java.util.HashMapオブジェクト(Java コードの呼び出しからの戻り値)がmあり、キーと値のペアを追加して新しいマップを取得したいと考えています。

Clojure マップの場合mは、次を使用できます。

(assoc m "key" "value")

しかし、それを試してみると、次のようになりHashMapます。

java.lang.ClassCastException: java.util.HashMap は clojure.lang.Associative にキャストできません

どちらでも運が悪いseq

(assoc (seq m) "key" "value")

java.lang.ClassCastException: clojure.lang.IteratorSeq は clojure.lang.Associative にキャストできません

私がそれを行うことができた唯一の方法は、HashMapの独自のものを使用することでしたputが、それは返さvoidれるため、明示的に返す必要がありますm:

(do (. m put "key" "value") m)

これは慣用的な Clojure コードではありませんm。また、新しいマップを作成するのではなく、変更しています。

HashMapより Clojure っぽい方法で aを操作するには?

4

4 に答える 4

35

Clojure は Java Collections を seq 可能にするため、 java.util.HashMapで Clojure シーケンス関数を直接使用できます。

ただし、assocclojure.lang.Associativeを想定しているため、最初にjava.util.HashMapをそれに変換する必要があります。

(assoc (zipmap (.keySet m) (.values m)) "key" "value")

編集:より簡単な解決策:

(assoc (into {} m) "key" "value")
于 2009-11-03T04:03:24.640 に答える
29

Java コードとやり取りしている場合は、.put. これは必ずしも大罪ではありません。Clojure はJava コードを簡単に操作できるようdoに具体的に提供します。.

assocClojure のデータ構造でのみ機能するのは、わずかな変更を加えた新しい (不変の) コピーを非常に安価に作成できるようにするために多くの作業が行われたためです。Java HashMap は、同じように機能することを意図していません。変更を加えるたびにクローンを作成し続ける必要があり、コストがかかる可能性があります。

本当に Java のミューテーションランドから抜け出したい場合 (たとえば、これらの HashMap を長期間保持していて、あちこちで Java 呼び出しを行いたくない場合や、 and を介してシリアル化する必要がある場合、または必要な場合print) readClojure STM を使用してスレッドセーフな方法でそれらを操作するには、Java HashMap と Clojure ハッシュマップの間で簡単に変換できます。これは、Clojure データ構造が適切な Java インターフェースを実装しているため、相互に通信できるためです。

user> (java.util.HashMap. {:foo :bar})
#<HashMap {:foo=:bar}>

user> (into {} (java.util.HashMap. {:foo :bar}))
{:foo :bar}

作業が完了したら、作業中のオブジェクトを返す のようなものが必要な場合doは、 を使用できますdoto。実際、Java HashMap は、この関数の公式ドキュメントの例として使用されています。これは、Java オブジェクトを (慎重に) 使用すれば、世界の終わりではないことを示しています。

clojure.core/doto
([x & forms])
Macro
  Evaluates x then calls all of the methods and functions with the
  value of x supplied at the front of the given arguments.  The forms
  are evaluated in order.  Returns x.

  (doto (new java.util.HashMap) (.put "a" 1) (.put "b" 2))

いくつかの可能な戦略:

  1. 可能であれば、ミューテーションと副作用を 1 つの関数に限定してください。関数が同じ入力に対して常に同じ値を返す場合、内部で必要なことは何でも実行できます。配列またはマップを変更することが、アルゴリズムを実装する最も効率的または簡単な方法である場合があります。副作用を世界に「漏らさない」限り、関数型プログラミングの利点を引き続き享受できます。

  2. オブジェクトがしばらく存在する場合、または他の Clojure コードとうまく連携する必要がある場合は、できるだけ早く Clojure データ構造に取り込んで、最後の 1 秒で Java HashMap にキャストし直すようにしてください (フィード時)。それらは Java に戻ります)。

于 2009-11-03T08:51:51.407 に答える
5

従来の方法で Java ハッシュ マップを使用してもまったく問題ありません。
(do (. m put "key" "value") m)
This is not idiomatic Clojure code, plus I'm modifying m instead of creating a new map.

実際に変更することを意図したデータ構造を変更しています。Java のハッシュ マップには、 Clojures マップの効率的なコピーを可能にする構造共有が欠けています。これを行う一般的な慣用的な方法は、java-interop 関数を使用して典型的な Java の方法で Java 構造を操作するか、それらを Clojure 構造にきれいに変換して、機能的な Clojure の方法で操作することです。もちろん、それが生活を楽にし、より良いコードにつながる場合を除きます。その後、すべての賭けはオフになります。

于 2009-11-03T19:37:03.793 に答える
2

これは、clojure バージョンと Java のメモリ特性を比較しようとしたときに、ハッシュマップを使用して作成したコードです (ただし、clojure から使用されます)。

(インポート '(java.util ハッシュテーブル))
(定義周波数 2 [coll]
    (let [mydict (新しいハッシュテーブル)]
      (reduce (fn [counts x]
            (let [y (.toLowerCase x)]
              (if (.get mydict y)
            (.put mydict y (+ (.get mydict y) 1))
            (.put mydict y 1)))) coll) mydict))

これは、いくつかのコレクションを取得し、それぞれの異なるもの (文字列内の単語など) が再利用された回数を返すためです。

于 2009-11-03T06:34:10.357 に答える