Clojure';DEFS记录-如何使用它?

Clojure';DEFS记录-如何使用它?,clojure,Clojure,我正试图用Clojure中的defrecord创建我自己的不可变数据类型/方法。目标是拥有一个数据类型,我可以创建它的实例,然后调用它的方法来返回一个带有变异变量的新副本。假设a和b是向量。我想更新两者中的一个值,并返回整个结构的一个新副本,同时更新这些向量。这显然不是编译,我只是想让大家了解我的想法 (defrecord MyType [a b] (constructor [N] ; I'd like to build an initial instance, creating a

我正试图用Clojure中的
defrecord
创建我自己的不可变数据类型/方法。目标是拥有一个数据类型,我可以创建它的实例,然后调用它的方法来返回一个带有变异变量的新副本。假设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
  )
)
我想调用构造函数,然后调用mutator尽可能多的次数(还有其他函数不进行求变,但我不想让问题变得更复杂)


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

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

(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]}
“变异和回归”也是免费的-您可以使用:


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

Clojure允许您创建记录,这些记录是自定义的、类似于地图的数据类型。 它们与地图相似,因为它们将键与值相关联,您可以使用与地图相同的方式查找它们的值,并且它们与地图一样是不可变的



太好了,谢谢。在返回之前,是否有一种简单/惯用的方法可以同时对两个向量进行变异?或者在中关联:
(assoc in foo[:b0]12)
assoc允许您同时执行多个键,例如
(assoc foo:a[7 8 9]:b[3 4 5])
。虽然通常情况下,如果您想进行任何更奇特/复杂的变异,您会希望将其封装在一个单独的(命名良好的)函数中。有时您需要一个组件,而不是defrecord提供的组件。只是说
(:firstname person1)
(:city (:address person1))
(defrecord Person [last first address])
;=> core.Person

(defrecord Ad [street city zip])
;=> core.Ad

(def p1 (Person. "Jhon" "Mick"
                 (Ad. "US187956" "NY" 3369)))
;=> #'core/p1

(update-in p1 [:address :zip] inc)
;=> #core.Person{:last "Jhon", :first "Mick", :address #playsync.core.Ad{:street "US187956", :city "NY", :zip 3370}}

(assoc p1 :last "Adam")
;=> #core.Person{:last "Adam", :first "Mick", :address #playsync.core.Ad{:street "US187956", :city "NY", :zip 3370}}