【问题标题】:which protocol defines conj in clojure?哪个协议在clojure中定义了conj?
【发布时间】:2017-12-28 02:26:09
【问题描述】:

假设我写了一个函数:

(defn foo [to x] (conj to x))

并希望通过声明to 必须实现某些协议来记录它(如结构/类型to 必须支持调用conj)。是否有包含此信息的网站或数据库?显然,我想将这个问题概括为“我在哪里可以找到所有 clojure 协议的完整参考?”

作为一个使用 Sam Estep 建议的明确而具体的示例,它看起来像:

(defn invert-many-to-one
  "returns a one-to-many mapping where vals are collections of type `(constructor-fn)`,
   (defaults to `hash-set`). Note that `constructor-fn` is a function of 0 args.
  `insert-fn` function can be passed. if only `constructor-fn` is passed
  then `insert-fn` defaults to `conj` and `(constructor-fn)` must be an instance
  of `clojure.lang.IPersistentCollection`"
  ([m] (invert-many-to-one hash-set conj m))
  ([constructor-fn m] {:pre [(instance? clojure.lang.IPersistentCollection (constructor-fn))]}
   (invert-many-to-one constructor-fn conj m))
  ([constructor-fn insert-fn m]
   (persistent!
    (reduce (fn [m [k v]]
              (assoc! m v (insert-fn (clojure.core/get m v (constructor-fn)) k)))
            (transient {}) m))))

【问题讨论】:

  • 老实说,我希望我能两次支持你的问题;我印象深刻。
  • 你的问题比较笼统,而且你得到的答案比我五年前给出的要好,所以我不会将其作为重复项关闭,但对于 conj 来说,这一直是之前问过:stackoverflow.com/q/8781213/625403.
  • @amalloy 无论如何...接口不是协议:P
  • 是的,但是“没有协议,它是一个接口”显然是同一个问题的答案。

标签: clojure protocols reference-manual


【解决方案1】:

不幸的是 protocols 直到 Clojure 1.2 才引入,到那时,所有内置的数据结构抽象都已经实现 as Java interfaces 而不是协议。这具有您所期望的缺点,但是虽然将所有这些抽象作为协议重新实现对于 ClojureScript 来说是合适的,因为它是在引入协议之后创建的,但将它们改进到 JVM Clojure 中是不可行的。

如果您查看conj 的源代码,您会看到它调用clojure.lang.RT/conj,这要求它的第一个参数实现IPersistentCollection 接口。因此,您可以这样编写函数:

(defn foo [to x]
  {:pre [(instance? clojure.lang.IPersistentCollection to)]}
  (conj to x))

为了您的概括,我会向您指出一个question,我在过去询问过实现 Clojure 的核心接口。如果答案不足以回答您的问题,请告诉我,我将在此处添加更多详细信息。

我会对你的 invert-many-to-one 函数做一些小的调整:

(defn invert-many-to-one
  "Returns a one-to-many mapping where vals are collections of type
  `(constructor-fn)` (defaults to `hash-set`). Note that `constructor-fn` is a
  function of 0 args. `insert-fn` function can be passed. If only
  `constructor-fn` is passed then `insert-fn` defaults to `conj`.
  `(constructor-fn)` must be an instance of
  `clojure.lang.IPersistentCollection`."
  ([m]
   (invert-many-to-one hash-set m))
  ([constructor-fn m]
   (invert-many-to-one constructor-fn conj m))
  ([constructor-fn insert-fn m]
   {:pre [(instance? clojure.lang.IPersistentCollection (constructor-fn))]}
   (persistent!
    (reduce (fn [m [k v]]
              (assoc! m v (insert-fn (get m v (constructor-fn)) k)))
            (transient {}) m))))
  • 我将 arity-1 主体改为调用 arity-2 主体而不是 arity-3 主体;这样,您只需在一处将conj 指定为默认值。
  • 我将前提条件移到了 arity-3 主体中,以便在所有情况下都使用它。
  • 出于惯用性,我将 clojure.core/get 替换为 get

当然,正如您在 cmets 中指出的那样,这三个更改各有其缺点,因此请务必对它们持保留态度。

【讨论】:

  • 很遗憾 clojure atlas 似乎不再存在。我找到了 clojure/java 的类/接口层次结构,但我不记得在哪里。
  • @beoliver 啊,我没注意到;这确实是不幸的!不过,looks like 你仍然可以访问它的内容。
  • 哈哈,当我粘贴到 stackoverflow 时,我卡住了 conj。决定我会节省几个 cpu 周期(并避免实例测试);) get 调用是因为阴影。如果您将条件转移到 arity-3,则可能会遇到 insert-fn 不需要它是 IPersistentCollection...
  • 考虑(invert-many-to-one #(transient #{}) conj! {1 1 3 1})
  • @beoliver 好点!我没想到。我在答案的末尾添加了免责声明。
猜你喜欢
  • 2017-09-09
  • 2015-10-25
  • 2014-08-21
  • 1970-01-01
  • 1970-01-01
  • 2017-04-28
  • 1970-01-01
  • 1970-01-01
  • 2011-12-21
相关资源
最近更新 更多