为什么Clojure中的disj和dissoc功能不同?

why are `disj` and `dissoc` distinct functions in Clojure?

据我所知,Clojure的核心功能几乎总是适用于不同类型的集合,例如 conjfirstrest等。我有些困惑,为什么disjdissoc不同? 它们具有完全相同的签名:

1
2
(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在很多情况下是如此优雅,以至于它在不是它的时候对我来说真的很震撼:)


只是为了与Arthur的答案相反:conj的定义更早(名称conj出现在core.clj的第82行与disj的1443和dissoc的1429),但仍适用于所有Clojure集合类型。 :-)显然,它不使用协议-而是使用常规的Java接口,就像大多数Clojure函数一样(事实上,我相信,目前Clojure中使用协议的唯一"核心"功能是reduce / < x6>)。

我猜想这是出于美学选择,实际上可能与地图支持conj的方式有关–如果地图支持disj,则可能希望它采用与传递给< x1>,这将是有问题的:

1
2
3
4
5
6
7
;; 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的东西"。

现在,从理论上讲dissoc可以用于集合,但是也许有人可能希望它们也支持assoc,这可能并没有什么意义。可能值得指出的是向量确实支持assoc而不是dissoc,因此它们并不总是在一起。这里肯定存在某种审美张力。


尝试回答他人的动机总是可疑的,尽管我强烈怀疑这是core.clj中的引导问题。这两个函数都是在core.clj的早期定义的,并且几乎相同,只是它们每个都只采用一种类型并直接对其调用方法。

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

1
(. set (disjoin key))

这两个函数都是在core.clj中定义协议之前定义的,因此它们不能使用协议根据类型在它们之间进行分配。在协议存在之前,语言规范中还定义了这两种方法。它们也经常被调用,以至于有强烈的动机使它们尽可能快。


1
2
3
4
5
6
7
8
9
10
11
12
  (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)))

谁在乎?只需使用上面的功能,而忘记过去...