【问题标题】:Implementing Custom Data Structures Using Clojure Protocols使用 Clojure 协议实现自定义数据结构
【发布时间】:2014-05-11 19:28:20
【问题描述】:

我可能错过了关于协议的全部要点,但我的问题是,协议是否可以用于指示如何迭代自定义数据结构或 println 如何打印对象?

假设一张地图有两个向量,

{:a [] :b []}

当第一次调用它时,我想从 :a 向量中获取,但是当在这个结构上 conj 时我想 conj 到 :b。我可以使用协议来实现这种行为吗?

【问题讨论】:

  • 目前还没有,因为几乎没有低级 fn(reduce 除外)被“协议化”,但您可以使用 defrecord 或 deftype 来定义行为如您所愿的数据类型。
  • 是的,但是我还必须实现作用于它的函数,创建冗余函数名称,例如 head 作为我的数据结构的第一个,不是吗?
  • 如果你使用 deftype,你可以为 Clojure 使用的各种接口(如 clojure.lang.ISeq)提供你想要的实现。

标签: clojure


【解决方案1】:

有些东西在 Clojure 中仍然作为 Java 接口实现;其中,我想说一些可能会永远保持这种状态,以便与来自其他 JVM 语言的 Clojure 代码协作。

幸运的是,当使用 deftype 定义类型时,您可以让新类型实现您需要的任何 Java 接口(Brian 在上面的评论中提到),以及 java.lang.Object 的任何方法。与您的描述相匹配的示例可能如下所示:

(deftype Foo [a b]
  clojure.lang.IPersistentCollection
  (seq [self] (if (seq a) self nil))
  (cons [self o] (Foo. a (conj b o)))
  (empty [self] (Foo. [] []))
  (equiv
   [self o]
   (if (instance? Foo o)
     (and (= a (.a o))
          (= b (.b o)))
     false))
  clojure.lang.ISeq
  (first [self] (first a))
  (next [self] (next a))
  (more [self] (rest a))
  Object
  (toString [self] (str "Foo of a: " a ", b: " b)))

您可以在 REPL 中使用它的示例:

user> (.toString (conj (conj (Foo. [] []) 1) 2))
"Foo of a: [], b: [1 2]"
user> (.toString (conj (conj (Foo. [:a :b] [0]) 1) 2))
"Foo of a: [:a :b], b: [0 1 2]"
user> (first (conj (conj (Foo. [:a :b] [0]) 1) 2))
:a
user> (Foo. [1 2 3] [:a :b :c])
(1 2 3)

请注意,REPL 将其打印为 seq;我相信这是因为clojure.lang.ISeq 的内联实现。您可以跳过它并将seq 方法替换为返回(seq a) 的方法,以使用自定义toString 打印表示。不过str 总是使用toString

如果您需要 pr 系列函数的自定义行为(包括 println 等),则必须考虑为您的类型实现自定义 print-methodprint-methodclojure.core 中定义的多方法;查看 Clojure 源代码中的 core_print.clj 示例实现。

【讨论】:

    【解决方案2】:

    我在玩自定义集合,想自定义 REPL 的输出,所以我最终听从了 Michal 关于最后一点的建议。我已经包含了有关如何执行此操作的代码 sn-p,因为我发现筛选源代码花了我一段时间,因为我没有发现其他任何地方对此进行了解释。

    (defmethod print-method your.custom.collection.goes.Here [c, ^java.io.Writer w]
        (.write w (str "here is my custom output: " c)))
    

    这很方便,例如,在 seq 总是打印带有括号的自定义向量(如 Michal 的示例)并且您需要方括号(如常规 Clojure 向量)的情况下:

    (defmethod print-method your.custom.Vector [v, ^java.io.Writer w]
        (.write w (str (into [] v))))
    

    这也意味着现在可以实现 seq 以实际返回您的数据类型的序列,而不必仅仅为 REPL 输出实现它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-04
      • 1970-01-01
      相关资源
      最近更新 更多