2012-08-14 31 views
19

我试图在Clojure中用defrecord创建我自己的不可变数据类型/方法。我们的目标是创建一个可以创建实例的数据类型,然后调用它的方法以使用变异变量返回自己的新副本。说a和b是向量。我想在两者中更新一个值,并返回整个结构的新副本,并更新这些向量。这显然不能编译,我只是试图让我的想法。Clojure的defrecord - 如何使用它?

(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 
) 
) 

我想调用构造函数,然后突变很多次我想(有没有发生变异等功能,但我不希望使之成为更复杂问题)。

另外,如果这不是惯用的Clojure,你应该如何做这样的事情?

回答

26

这里是你如何定义你的记录:

(defrecord MyType [a b]) 

注意,Clojure中你通常不会定义你的记录类型本身的“方法”(例外的是,如果你想直接实现一个Java接口或一个协议)。

一个基本的构造(与->前缀)获取免费自动生成:

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

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

然后,您可以编写利用这一点,如更复杂的构造函数

(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]} 

和 “发生变异 - 返回” 还配备了免费的 - 你可以用assoc

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

优秀的,谢谢。在返回之前是否有一种简单/惯用的方法来同时变异两个载体? – 2012-08-14 18:25:32

+2

或assoc-in:'(assoc-in foo [:b 0] 12)' – Kevin 2012-08-14 18:27:00

+2

Assoc允许您一次执行多个密钥,例如, '(assoc foo:a [7 8 9]:b [3 4 5])''。虽然通常如果你想做更多的花哨/复杂的变异,你会想把它包装在一个单独的(名字很好的)函数中。 – mikera 2012-08-14 18:27:28

2

Clojure的defrecord例如:

;;定义地址记录

(defrecord Address [city state]) 

;;定义人员记录

(defrecord Person [firstname lastname ^Address address]) 

;;打造专业化的构造

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

;;创建人

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

;;检索值

(:firstname person1) 
(:city (:address person1))