Clojure:从Map分配defrecord字段

时间:2010-12-23 15:55:53

标签: constructor clojure

关注How to make a record from a sequence of values,您如何编写defrecord构造函数调用并分配Map中的字段,留下未命名的字段nil

(defrecord MyRecord [f1 f2 f3])
(assign-from-map MyRecord {:f1 "Huey" :f2 "Dewey"})  ; returns a new MyRecord

我想可以写一个宏来做这件事。

3 个答案:

答案 0 :(得分:11)

您可以简单地merge将地图放入使用nil s初始化的记录中:

(merge (MyRecord. nil nil nil) {:f1 "Huey" :f2 "Dewey"})

请注意,记录能够以类似地图的方式保存存储在额外键下的值。

可以使用反射获取记录字段列表:

(defn static? [field]
  (java.lang.reflect.Modifier/isStatic
   (.getModifiers field)))

(defn get-record-field-names [record]
  (->> record
       .getDeclaredFields
       (remove static?)
       (map #(.getName %))
       (remove #{"__meta" "__extmap"})))

后一个函数返回一系列字符串:

user> (get-record-field-names MyRecord)
("f1" "f2" "f3")

__meta__extmap是Clojure记录分别用于保存元数据和支持地图功能的字段。

你可以写点像

(defmacro empty-record [record]
  (let [klass (Class/forName (name record))
        field-count (count (get-record-field-names klass))]
    `(new ~klass ~@(repeat field-count nil))))

并使用它来创建记录类的空实例,如下所示:

user> (empty-record user.MyRecord)
#:user.MyRecord{:f1 nil, :f2 nil, :f3 nil}

完全限定名称在此非常重要。只要在编辑引用它的任何empty-record表单时声明了记录类,它就会起作用。

如果将empty-record写成函数,可以让它期望一个实际的类作为参数(避免“完全限定”的问题 - 你可以用给定的方式命名你的类上下文),但代价是在运行时进行反射。

答案 1 :(得分:4)

当定义记录时,Clojure会生成 map-> RecordType 函数。

(defrecord Person [first-name last-name])
(def p1 (map->Person {:first-name "Rich" :last-name "Hickey"}))

映射不需要定义记录定义中的所有字段,在这种情况下,缺少的键在结果中具有nil值。地图还允许包含不属于记录定义的额外字段。

答案 2 :(得分:1)

如链接问题回复中所述,code here显示了如何创建defrecord2宏以生成一个带有地图的构造函数,如所示here。特别感兴趣的是make-record-constructor宏。