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

时间:2015-03-04 00:31:19

标签: clojure

就我所见,Clojure的核心功能几乎总是适用于不同类型的收藏,例如: conjfirstrest等。我有点疑惑disjdissoc为何不同;他们有完全相同的签名:

(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在很多时候非常优雅,只要它不是,它就会对我很不利:)

3 个答案:

答案 0 :(得分:7)

为了给Arthur的答案提供一个平衡点:conj甚至更早定义(名称conj出现在core.clj的第82行vs.1443 disj和{14} for { {1}})但适用于所有Clojure集合类型。 :-)显然它不使用协议 - 而是使用常规Java接口,就像大多数Clojure函数一样(事实上我相信目前Clojure中唯一使用协议的“核心”功能是dissoc / 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 ) {}还是{: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,所以这些并不总是在一起;这里肯定有一些审美紧张。

答案 1 :(得分:4)

尽管我强烈怀疑这是core.clj中的一个引导问题,但尝试回答别人的动机总是可疑的。这两个函数都是在core.clj中很早就定义的,除了它们各自只采用一种类型并直接在其上调用方法之外几乎相同。

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

(. set (disjoin key))

core.clj中定义protocals 之前定义了这两个函数,因此它们不能使用协议根据类型在它们之间进行分派。这两个都存在于协议之前的语言规范中。它们也经常被呼叫,以便有尽可能快地制作它们的强烈动机。

答案 2 :(得分: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)))

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