1

我已经定义了一个包含一堆字段的记录——其中一些是计算出来的,其中一些不直接映射到我正在摄取的 JSON 数据中的键。我正在为它编写一个工厂函数,但我希望有合理的默认/未找到值。有没有更好的方法来应对:or [field1 "" field2 "" field3 "" field4 ""...]?我可以写一个宏,但如果我不需要,我宁愿不写。

4

2 回答 2

1

在构造函数中实现默认值有三种常见的习惯用法。

  1. :or解构

    例子:

    (defn make-creature [{:keys [type name], :or {type :human
                                                  name (str "unnamed-" (name type))}}]
      ;; ...
      )
    

    当您想内联指定默认值时,这很有用。作为奖励,它允许在地图中根据矢量对 kv进行let样式绑定。:or:keys

  2. 合并

    例子:

    (def default-creature-spec {:type :human})
    
    (defn make-creature [spec]
       (let [spec (merge default-creature-spec
                         spec)]
          ;; ....
          ))
    

    当您想在外部定义默认值、在运行时生成它们和/或在其他地方重用它们时,这很有用。

  3. 简单的or

    例子:

    (defn make-creature [{:keys [type name]}]
      (let [type (or type :human)
            name (or name (str "unnamed-" (name type)))]
         ;; ...
         ))
    

    这与解构一样有用,:or但只评估实际需要的那些默认值,即它应该用于计算默认值会增加不必要的开销的情况。(我不知道为什么要:or评估所有默认值(从 Clojure 1.7 开始),所以这是一种解决方法)。

于 2015-07-26T05:43:29.943 回答
0

如果您真的希望所有字段都具有相同的默认值,并且它们确实必须不同于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”,并且在讨论如何从记录中获取密钥时也在此线程中提到。因此,由您决定使用它是否是一个好主意)

于 2015-07-25T23:25:48.903 回答