我已经定义了一个包含一堆字段的记录——其中一些是计算出来的,其中一些不直接映射到我正在摄取的 JSON 数据中的键。我正在为它编写一个工厂函数,但我希望有合理的默认/未找到值。有没有更好的方法来应对:or [field1 "" field2 "" field3 "" field4 ""...]
?我可以写一个宏,但如果我不需要,我宁愿不写。
2 回答
在构造函数中实现默认值有三种常见的习惯用法。
:or
解构例子:
(defn make-creature [{:keys [type name], :or {type :human name (str "unnamed-" (name type))}}] ;; ... )
当您想内联指定默认值时,这很有用。作为奖励,它允许在地图中根据矢量对 kv进行
let
样式绑定。:or
:keys
合并
例子:
(def default-creature-spec {:type :human}) (defn make-creature [spec] (let [spec (merge default-creature-spec spec)] ;; .... ))
当您想在外部定义默认值、在运行时生成它们和/或在其他地方重用它们时,这很有用。
简单的
or
例子:
(defn make-creature [{:keys [type name]}] (let [type (or type :human) name (or name (str "unnamed-" (name type)))] ;; ... ))
这与解构一样有用,
:or
但只评估实际需要的那些默认值,即它应该用于计算默认值会增加不必要的开销的情况。(我不知道为什么要:or
评估所有默认值(从 Clojure 1.7 开始),所以这是一种解决方法)。
如果您真的希望所有字段都具有相同的默认值,并且它们确实必须不同于nil
,并且您不想再次将它们写下来,那么您可以通过调用keys
一个空实例来获取记录字段,然后构造一个默认值与实际值合并的地图:
(defrecord MyFancyRecord [a b c d])
(def my-fancy-record-fields (keys (map->MyFancyRecord {})))
;=> (:a :b :c :d)
(def default-fancy-fields (zipmap my-fancy-record-fields (repeat "")))
(defn make-fancy-record [fields]
(map->MyFancyRecord (merge default-fancy-fields
fields)))
(make-fancy-record {})
;=> {:a "", :b "", :c "", :d ""}
(make-fancy-record {:a 1})
;=> {:a 1, :b "", :c "", :d ""}
要获取记录字段列表,您还可以getBasis
在记录类上使用静态方法:
(def my-fancy-record-fields (map keyword (MyFancyRecord/getBasis)))
(getBasis
不是公共记录 api 的一部分,因此不能保证它不会在未来的 clojure 版本中被删除。现在它在 clojure 和clojurescript中都可用,它的用法在“Clojure programming by Chas Emerick, Brian Carper, Christophe Grand”,并且在讨论如何从记录中获取密钥时也在此线程中提到。因此,由您决定使用它是否是一个好主意)