使用Clojure协议实现自定义数据结构

Ham*_*aya 10 clojure

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

假设有两个向量的地图,

{:a [] :b []}
Run Code Online (Sandbox Code Playgroud)

当我第一次调用它时,我想从:一个向量中取出,但是当我想要结合这个结构时,我想联合:b.我可以使用协议来实现这种行为吗?

Mic*_*zyk 13

有些东西仍然在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)))
Run Code Online (Sandbox Code Playgroud)

您可以在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)
Run Code Online (Sandbox Code Playgroud)

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

如果您需要pr家庭功能的自定义行为(包括println等),您将不得不考虑print-method为您的类型实现自定义.print-method是一种多方法clojure.core; 在Clojure的源代码中查看core_print.clj以获取示例实现.