Domanda

Ho un java.util.HashMap oggetto m (un valore restituito da una chiamata al codice Java) e vorrei ottenere una nuova mappa con una coppia chiave-valore aggiuntiva.

Se m se fosse una mappa Clojure, potrei usare:

(assoc m "key" "value")

Ma provandolo su a HashMap dà:

java.lang.ClassCastException:java.util.HashMap non può essere trasmesso a clojure.lang.Associative

Nessuna fortuna con seq O:

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

java.lang.ClassCastException:clojure.lang.IteratorSeq non può essere trasmesso a clojure.lang.Associative

L'unico modo in cui sono riuscito a farlo è stato usare HashMapè proprio put, ma questo ritorna void quindi devo restituire esplicitamente m:

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

Questo non è il codice Clojure idiomatico, inoltre lo sto modificando m invece di creare una nuova mappa.

Come lavorare con a HashMap in un modo più Clojureiano?

È stato utile?

Soluzione

Clojure rende le collezioni di Java ss-in grado, in modo da poter utilizzare direttamente le funzioni di sequenza Clojure sul java.util.HashMap .

Ma assoc si aspetta un clojure.lang.Associative in modo da dovrete prima convertire il java.util.HashMap a che:

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

Edit: soluzione più semplice:

(assoc (into {} m) "key" "value")

Altri suggerimenti

Se si interfaccia con il codice Java, potrebbe essere necessario stringere i denti e farlo nel modo Java, usando .put. Questo non è necessariamente un peccato mortale; Clojure ti dà le cose come do e . specificamente in modo da poter lavorare con il codice Java con facilità.

assoc funziona solo su strutture dati Clojure perché un sacco di lavoro è andato in rendendo molto a buon mercato per creare nuove copie (immutabili) di loro con lievi alterazioni. Java HashMaps non sono destinati a lavorare nello stesso modo. Dovreste tenere la clonazione di loro ogni volta che si effettua un'alterazione, che può essere costoso.

Se si vuole veramente uscire da Java mutazione suolo (ad esempio forse si stanno tenendo questi HashMaps in giro per molto tempo e non vogliono Java chiamate in tutto il luogo, o avete bisogno di serializzare loro tramite print e read, o se si vuole lavorare con loro in un modo thread-safe utilizzando il Clojure STM) è possibile convertire tra Java HashMaps e Clojure hash-mappe abbastanza facilmente, perché le strutture di dati Clojure implementano le interfacce Java giusti in modo che possano parlare tra di loro .

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

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

Se si vuole una cosa do-like che restituisce l'oggetto su cui stai lavorando, una volta che hai finito di lavorare su di esso, è possibile utilizzare doto. In realtà, un HashMap Java viene utilizzato come esempio nella documentazione ufficiale per questa funzione, che è un'altra indicazione che non è la fine del mondo se si utilizzano gli oggetti Java (giudizio).

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))

Alcuni possibili strategie:

  1. Limitare il mutazione e gli effetti collaterali di una singola funzione, se potete. Se la funzione restituisce sempre lo stesso valore a parità di input, si può fare ciò che vuole internamente. A volte mutando una matrice o una mappa è il modo più efficiente o più semplice da implementare un algoritmo. Sarà ancora godere dei benefici della programmazione funzionale finché non si effetti collaterali "fuga" verso il resto del mondo.

  2. Se gli oggetti stanno per essere in giro per un po 'o di cui hanno bisogno di giocare bene con altro codice Clojure, cercare di farli in strutture di dati Clojure più presto possibile, e gettarli di nuovo in Java HashMaps a all'ultimo secondo (quando si alimenta di nuovo a Java).

È del tutto OK utilizzare la mappa hash Java in modo tradizionale.
(do (. m put "key" "value") m)
This is not idiomatic Clojure code, plus I'm modifying m instead of creating a new map.

Stai modificando una struttura dati che in realtà è destinata ad essere modificata. La mappa hash di Java non dispone della condivisione strutturale che consente di copiare in modo efficiente le mappe di Clojures.Il modo generalmente idiomatico per farlo è utilizzare le funzioni Java-Interop per lavorare con le strutture Java nel tipico modo Java, o convertirle in modo pulito in strutture Clojure e lavorare con esse nel modo funzionale Clojure.A meno che, naturalmente, ciò non renda la vita più semplice e non si traduca in un codice migliore;allora tutte le scommesse sono annullate.

Questo è un codice che ho scritto usando HashMaps quando stavo cercando di confrontare le caratteristiche di memoria della versione clojure vs Java (ma usato da clojure)

(import '(java.util Hashtable))
(defn frequencies2 [coll]
    (let [mydict (new Hashtable)]
      (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))

Questo è quello di prendere un po 'di raccolta e tornare quante volte ogni cosa diversa (diciamo una parola in una stringa) viene riutilizzato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top