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

kor*_*rok 6 clojure

因为我见过到目前为止,Clojure的核心功能几乎都是针对不同类型的集合,例如工作conj,first,rest等我有点疑惑,为什么disjdissoc是不同的,虽然; 他们有完全相同的签名:

(dissoc map) (dissoc map key) (dissoc map key & ks)
(disj set) (disj set key) (disj set key & ks)
Run Code Online (Sandbox Code Playgroud)

和相当类似的语义.为什么这些都不是由同一个功能覆盖?我能看到的唯一一个支持这一点的论点是,map既有(assoc map key val)并且都有(conj map [key val])添加条目,而set只支持(conj set k).

我可以编写一个单行函数来处理这种情况,但是Clojure在很多时候都是如此优雅,以至于每当它不是:)时它真的很刺激我

Mic*_*zyk 7

只是为Arthur的答案提供一个平衡点:conj甚至更早定义(名称conj出现在core.clj的第82行vs.1443 for disj和1429 for dissoc),但适用于所有Clojure集合类型.:-)显然它不使用协议 - 而是使用常规Java接口,就像大多数Clojure函数一样(事实上我相信当前Clojure中使用协议的唯一"核心"功能是reduce/ reduce-kv).

我猜想这是由于一种美学选择,并且确实可能​​与地图支持的方式有关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
      )
Run Code Online (Sandbox Code Playgroud)

应该返回{},{:foo 1 [:foo 1] 2}还是{{:foo 1 [:foo 1] 2} 3}conj愉快地接受[:foo 1]{:foo 1 [:foo 1] 2}作为conj地图的东西.(conj有两个map参数的意思merge;确实merge是实现的conj,加上特殊处理nil).

因此,dissoc对地图来说也许是有道理的,因此它很明显它会删除一个键而不是"可能是某些东西conj".

现在,理论上dissoc可以在集合上工作,但也许人们可能会期望它们也支持assoc,这可能没有用.值得指出的是,向量确实支持assoc而不是dissoc,所以这些并不总是在一起; 这里肯定存在一些审美紧张.