关于Clojure defrecord:Clojure defrecord-如何使用?

Clojure's defrecord - how to use it?

我正在尝试使用Clojure中的defrecord创建自己的不可变数据类型/方法。 我们的目标是拥有一个可以创建其实例的数据类型,然后调用其方法以返回具有突变变量的自身的新副本。 说a和b是向量。 我想同时更新两个值,并使用更新的向量返回整个结构的新副本。 这显然不能编译,我只是想把我的想法传播出去。

1
2
3
4
5
6
7
8
9
(defrecord MyType [a b]
  (constructor [N]
    ; I'd like to build an initial instance, creating a and b as vectors of length N
  )

  (mutate-and-return []
    ; I'd like to mutate (assoc the vectors) and return the new structure, a and b modified
  )
)

我想先调用构造函数,然后再调用mutator(还有其他一些不可变的函数,但我不想让这个问题更复杂)。

或者,如果这不是惯用的Clojure,您应该如何做这样的事情?


定义记录的方法如下:

1
(defrecord MyType [a b])

请注意,在Clojure中,通常不会在记录类型本身内定义"方法"(例外是如果您想直接实现Java接口或协议)。

免费自动生成一个基本构造函数(以->前缀):

1
2
3
4
(def foo (->MyType [1 2 3] [4 5 6]))

foo
=> #user.MyType{:a [1 2 3], :b [4 5 6]}

然后,您可以编写使用此功能的更复杂的构造函数,例如

1
2
3
4
5
6
7
(defn mytype-with-length [n]
  (let [a (vec (range n))
        b (vec (range n))]
    (->MyType a b)))

(mytype-with-length 3)
=> #user.MyType{:a [0 1 2], :b [0 1 2]}

而且"变异和返回"也是免费提供的-您可以使用asoc:

1
2
(assoc foo :b [7 8 9])
=> user.MyType{:a [1 2 3], :b [7 8 9]}


Clojure defrecord示例:

;;定义地址记录

1
(defrecord Address [city state])

;;定义人员记录

1
(defrecord Person [firstname lastname ^Address address])

;;购买了构造函数

1
2
(defn make-person ([fname lname city state]
               (->Person fname lname (->Address city state))))

;;创建一个人

1
(def person1 (make-person"John""Doe""LA""CA"))

;;检索值

1
2
(:firstname person1)
(:city (:address person1))