2015-03-04 11 views
6

Soweit ich gesehen habe, arbeiten Clojure Kernfunktionen fast immer für verschiedene Arten der Sammlung, z. conj, first, rest, etc. Ich bin ein wenig verwirrt, warum disj und dissoc sind jedoch unterschiedlich; sie haben die exakt gleiche Signatur:Warum sind `disj` und` dissoz` unterschiedliche Funktionen in Clojure?

(dissoc map) (dissoc map key) (dissoc map key & ks) 
(disj set) (disj set key) (disj set key & ks) 

und ziemlich ähnliche Semantik. Warum sind diese nicht beide durch die gleiche Funktion abgedeckt? Das einzige Argument, das ich dafür sehen kann, ist, dass Karten sowohl (assoc map key val) als auch (conj map [key val]) Einträge hinzufügen können, während Sets nur (conj set k) unterstützen.

kann ich eine einzeilige Funktion schreiben, um diese Situation zu handhaben, aber Clojure sind so elegant, so viel von der Zeit, dass es wirklich für mich ist schrill, wenn es nicht

Antwort

5

Nur ein Gegengewicht zu Arthurs Antwort zu geben: conj früher definiert ist sogar (der Name conj erscheint auf der Leitung 82 von core.clj vs.1443 für disj und 1429 für dissoc) und arbeitet noch auf allen Kollektionstypen Clojure. :-) Klar, es verwendet keine Protokolle - stattdessen verwendet es eine normale Java-Schnittstelle, wie die meisten Clojure-Funktionen (in der Tat glaube ich, dass derzeit das einzige Stück "Kern" -Funktionalität in Clojure, die Protokolle verwendet reduce/reduce-kv).

ich vermuten würde, dass es zu einer ästhetischen Wahl aufgrund ist, und in der Tat wahrscheinlich auf die Art und Weise im Zusammenhang, in dem Bebauungsmatrizen conj - wurden sie disj unterstützen, könnte man erwarten, dass es die gleichen Argumente nehmen, die zu conj weitergegeben werden konnten

;; hypothetical disj on map 
(disj {:foo 1 
     [:foo 1] 2 
     {:foo 1 [:foo 1] 2} 3} 
     } 
     {:foo 1 [:foo 1] 2} ;; [:foo 1] similarly problematic 
    ) 

Sollte die Rückkehr {}, {:foo 1 [:foo 1] 2} oder {{:foo 1 [:foo 1] 2} 3}:, was problematisch wäre? conj akzeptiert gerne [:foo 1] oder {:foo 1 [:foo 1] 2} als Dinge zu conj auf einer Karte. (conj mit zwei Kartenargumenten bedeutet merge; in der Tat merge ist in Bezug auf conj implementiert, spezielle Behandlung von nil hinzufügen).

So macht es vielleicht Sinn, dissoc für Karten zu haben, so dass es klar ist, dass es einen Schlüssel entfernt und nicht "etwas, das conj 'd sein könnte".Jetzt

, theoretisch könnte dissoc gemacht werden auf Sätze zu arbeiten, aber dann vielleicht könnte man erwarten, dass sie auch assoc unterstützen, die wohl nicht wirklich Sinn machen würde. Es lohnt sich, darauf hinzuweisen, dass Vektoren assoc und nicht dissoc unterstützen, so dass diese nicht immer zusammenpassen; da ist sicher eine gewisse ästhetische Spannung.

4

ist :) Es ist immer zweifelhaft zu versuchen, antworte auf die Motivationen anderer, obwohl ich den Verdacht hege, dass es sich um ein Bootstrapping-Problem in core.clj handelt. Beide Funktionen sind ziemlich früh in core.clj definiert und sind fast identisch, außer dass sie jeweils genau einen Typ annehmen und eine Methode direkt aufrufen.

(. clojure.lang.RT (dissoc map key)) 

und

(. set (disjoin key)) 

beide dieser Funktionen werden definiert vor protocals definiert sind in core.clj so können sie nicht ein Protokoll verwenden, um zwischen ihnen basierend auf Art zu versenden. Beide waren auch in der Sprachspezifikation definiert, bevor Protokolle existierten. Sie werden auch beide oft genug genannt, dass es einen starken Anreiz geben würde, sie so schnell wie möglich zu machen.

1
(defn del 
    "Removes elements from coll which can be set, vector, list, map or string" 
    [ coll & rest ] 
    (let [ [ w & tail ] rest ] 
    (if w 
     (apply del (cond 
      (set? coll) (disj coll w) 
      (list? coll) (remove #(= w %) coll) 
      (vector? coll) (into [] (remove #(= w %) coll)) 
      (map? coll) (dissoc coll w) 
      (string? coll) (.replaceAll coll (str w) "")) tail) 
      coll))) 

Wen kümmert es? Verwenden Sie einfach Funktion oben und vergessen Sie die Vergangenheit ...

Verwandte Themen