【问题标题】:Clojure: Assigning defrecord fields from MapClojure:从 Map 分配 defrecord 字段
【发布时间】:2015-11-08 23:15:36
【问题描述】:

跟进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

我想可以编写一个宏来执行此操作。

【问题讨论】:

  • 需要说明的是,从clojure 1.3.0开始,你可以做到(map->MyRecord {:f1 "Huey", :f2 "Dewey"})或者#user.MyRecord{:f1 "Huey", :f2 "Dewey"}

标签: constructor clojure


【解决方案1】:

您可以简单地将merge 映射到使用nils 初始化的记录中:

(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 被写成一个函数,人们可以让它期望一个实际的类作为参数(避免“完全限定”问题——你可以在给定的上下文中以任何方便的方式命名你的类) ,但代价是在运行时进行反射。

【讨论】:

  • 记录实现协议时,会在记录中添加一些其他字段。您可以将get-record-field-names 的最后一次remove 调用替换为(remove #(re-find #"__meta|__extmap|__cached" %))))
【解决方案2】:

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

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

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

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