11 Stimmen

In Clojure, wie können alle Schlüssel einer Karte zerstückelt werden?

In Clojure ist es möglich, einige Schlüssel einer Karte so zu destrukturieren:

(let [{:keys [cpp js]} {:cpp 88 :js 90}] 
   (println js); 90
   (println cpp); 88
 )

Gibt es eine Möglichkeit, alle Schlüssel einer Karte zu destrukturieren?

Vielleicht so etwas wie:

(let [{:all-the-keys} {:cpp 88 :js 90}] 
   (println js); 90
   (println cpp); 88
 )

25voto

amalloy Punkte 82950

Nicht wirklich, und es wäre keine gute Idee. Stell dir vor:

(let [{:all-the-keys} m]
  (foo bar))

Sind foo und bar global? Lokal? Schlüssel, die aus m extrahiert werden sollen? Was soll dieser Code tun, wenn m manchmal einen foo-Schlüssel enthält und foo auch eine globale Funktion ist? Manchmal rufen Sie die globale Funktion auf und manchmal rufen Sie die in m gespeicherte Funktion auf?

Abgesehen von den technischen Problemen (die überwunden werden könnten) ist es wirklich eine Katastrophe für Lesbarkeit und Vorhersehbarkeit. Seien Sie einfach explizit darüber, welche Schlüssel Sie extrahieren möchten; wenn Sie häufig dieselben zehn Schlüssel extrahieren möchten, können Sie eine einfache Makro wie (with-person p body) schreiben, die diesen gemeinsamen Fall für Sie vereinfacht.

11voto

one-more-minute Punkte 1398

Diese Frage ist ziemlich alt, daher haben Sie wahrscheinlich vergessen, aber sie ist auf Google aufgetaucht, als ich versuchte, dasselbe zu tun. Wenn ich also meine Lösung veröffentliche, könnte sie jemand anderem helfen.

(defmacro let-map [vars & forms]
  `(eval (list 'let (->> ~vars keys
                         (map (fn [sym#] [(-> sym# name symbol) (~vars sym#)]))
                         (apply concat) vec)
               '~(conj forms 'do))))

Dies wandelt im Grunde die Karte {:cpp 88 :js 90} in die Bindungsform [cpp 88 js 90] um, erstellt dann eine let-Bindung und führt einige Eval-Jitsu durch, um sicherzustellen, dass dies zur Laufzeit geschieht.

(def test-map {:cpp 88 :js 90})
(let-map test-map
  (println js)
  (println cpp))
;=> 90
;=> 88

7voto

mikera Punkte 103423

Sie könnten eine Makro schreiben, um dies zu tun (effektiv die Erstellung einer Mini-DSL), aber ich denke, das ist keine sehr gute Idee aus folgenden Gründen:

  • Um die richtigen Kompilierzeiten-Literale js und cpp zu erstellen, müssten Sie den Map zur Kompilierzeit destrukturieren. Dies wäre in Bezug darauf ziemlich einschränkend, was Sie damit tun könnten (Sie müssten die Schlüssel im Voraus angeben und es könnte beispielsweise nicht in höheren Funktionen verwendet werden)
  • Makros sind im Allgemeinen keine gute Idee, wenn eine einfachere Methode die Arbeit erledigen würde (siehe unten)

Ich würde einfach empfehlen, in Ihrem Fall ein einfaches doseq zu verwenden, um über die Map zu iterieren:

(let [my-map {:cpp 88 :js 90}]
  (doseq [[k v] my-map]
    (println v)))

Beachten Sie, dass:

  • Sie die Dekonstruktion wie oben verwenden können, um sowohl den Schlüssel k als auch den Wert v aus jedem Map-Eintrag zu extrahieren
  • Ich habe doseq anstelle von for verwendet, weil es nicht träge ist und es in diesem Beispiel so aussieht, als ob Sie die Schleife nur für die println Nebeneffekte verwenden.
  • Wenn Sie stattdessen eine träge Folge von Werten (88 90) möchten, wäre for angemessen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X