Clojure的defrecord - 如何使用它?

Eve*_*man 22 clojure

我正在尝试使用defrecordClojure 创建自己的不可变数据类型/方法.目标是拥有一个我可以创建实例的数据类型,然后调用其方法以返回带有变异变量的自身的新副本.说a和b是向量.我想更新两者中的值并返回整个结构的新副本,并更新这些向量.这显然不会编译,我只是试图让我的想法得到解决.

(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
  )
)
Run Code Online (Sandbox Code Playgroud)

我想按照我的意愿多次调用构造函数然后调用mutator(还有其他函数不会变异,但我不想让问题变得更复杂).

或者,如果这不是惯用的Clojure,你应该怎么做这样的事情呢?

mik*_*era 30

以下是您定义记录的方式:

(defrecord MyType [a b])
Run Code Online (Sandbox Code Playgroud)

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

基本构造函数(前缀->)会自动生成:

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

foo
=> #user.MyType{:a [1 2 3], :b [4 5 6]}
Run Code Online (Sandbox Code Playgroud)

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

(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]}
Run Code Online (Sandbox Code Playgroud)

并且"mutate-and-return"也是免费的 - 你可以使用assoc:

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

  • 或者联合:`(assoc-in foo [:b 0] 12)` (2认同)
  • Assoc允许你一次做多个键,例如``assoc foo:a [7 8 9]:b [3 4 5])`.虽然通常如果你想做更多花哨/复杂的变异,你可能希望将它包装在一个单独的(命名良好的)函数中. (2认同)

raz*_*van 5

Clojure defrecord示例:

;;定义地址记录

(defrecord Address [city state])
Run Code Online (Sandbox Code Playgroud)

;;定义人员记录

(defrecord Person [firstname lastname ^Address address])
Run Code Online (Sandbox Code Playgroud)

;;购买了构造函数

(defn make-person ([fname lname city state]
               (->Person fname lname (->Address city state))))
Run Code Online (Sandbox Code Playgroud)

;;创建一个人

(def person1 (make-person "John" "Doe" "LA" "CA"))
Run Code Online (Sandbox Code Playgroud)

;;检索值

(:firstname person1)
(:city (:address person1))
Run Code Online (Sandbox Code Playgroud)