【问题标题】:Clojure's defrecord - how to use it?Clojure defrecord - 如何使用它?
【发布时间】:2012-08-14 17:57:21
【问题描述】:

我正在尝试在 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 提供的。只是说

标签: clojure


【解决方案1】:

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

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

而且“mutate-and-return”也是免费提供的——你可以使用assoc

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

【讨论】:

  • 太好了,谢谢。有没有一种简单/惯用的方法可以在返回之前同时改变两个向量?
  • 或关联:(assoc-in foo [:b 0] 12)
  • Assoc 可让您一次执行多个键,例如(assoc foo :a [7 8 9] :b [3 4 5])。尽管通常如果您想做更多花哨/复杂的突变,您可能希望将其包装在一个单独的(命名良好的)函数中。
【解决方案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))

【讨论】:

    【解决方案3】:

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


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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-02
      • 2011-08-13
      • 1970-01-01
      相关资源
      最近更新 更多