2015-03-04 2 views
6

Насколько я видел, основные функции Clojure почти всегда работают для разных типов коллекций, например. conj, first, rest и т. Д. Я немного озадачен, почему disj и dissoc отличаются друг от друга; у них есть та же подпись:Почему функции `disj` и` dissoc` отличаются от Clojure?

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

и довольно схожая семантика. Почему они не охватываются одной и той же функцией? Единственный аргумент, который я могу видеть в пользу этого, состоит в том, что карты имеют как (assoc map key val), так и (conj map [key val]), чтобы добавлять записи, в то время как наборы поддерживают только (conj set k).

я могу написать одну функцию строки справиться с этой ситуацией, но Clojure так изысканно так много времени, что это действительно сотрясением мне всякий раз, когда это не :)

ответ

5

Просто, чтобы обеспечить противовесом ответ Артура: conj определяется еще раньше (название conj на линии 82 core.clj vs.1443 появляется для disj и 1429 для dissoc), и все же работает на всех типах коллекций Clojure. :-) Очевидно, что он не использует протоколы - вместо этого он использует обычный интерфейс Java, как и большинство функций Clojure (на самом деле я считаю, что в настоящее время единственной частью «основной» функциональности в Clojure, использующей протоколы, является reduce/).

Я бы предположить, что это связано с эстетическим выбором, и на самом деле, вероятно, связано с тем, как карты поддерживают conj - они должны были поддерживать disj, можно было бы ожидать, что она принимает те же аргументы, которые могут быть переданы в conj , что было бы проблематично:

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

Если это возвращение {}, {:foo 1 [:foo 1] 2} или {{:foo 1 [:foo 1] 2} 3}? conj счастливо принимает [:foo 1] или {:foo 1 [:foo 1] 2} как вещи к conj на карте. (conj с двумя аргументами карты означает merge; действительно merge реализован в терминах conj, добавив специальную обработку nil).

Так что, возможно, имеет смысл иметь dissoc для карт, чтобы было ясно, что он удаляет ключ, а не «что-то, что может быть conj 'd».

Теперь теоретически dissoc может быть выполнен для работы с наборами, но тогда, возможно, можно ожидать, что они также будут поддерживать assoc, что, возможно, не имеет смысла. Возможно, стоит отметить, что векторы поддерживают assoc, а не dissoc, поэтому они не всегда идут вместе; здесь есть определенная эстетическая напряженность.

4

Это всегда сомнительно, чтобы попытаться ответ на мотивы других, хотя я сильно подозреваю, что это проблема начальной загрузки в core.clj. обе эти функции определены довольно рано в core.clj и почти идентичны, за исключением того, что каждый из них принимает ровно один тип и вызывает метод на нем напрямую.

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

и

(. set (disjoin key)) 

обе эти функции определены до protocals определены в core.clj поэтому они не могут использовать протокол для отправки между ними в зависимости от типа. Оба эти параметра также определены в спецификации языка до того, как существуют протоколы. Их также называют достаточно часто, что будет сильный стимул сделать их как можно быстрее.

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

Кому это нужно? Просто используйте функцию выше и забудьте о прошлом ...

Смежные вопросы