clojure相同的“:或”所有键的值

时间:2015-07-25 18:20:14

标签: clojure destructuring

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

2 个答案:

答案 0 :(得分:1)

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

  1. :or析构

    示例:

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

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

  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),所以这是一种解决方法。

答案 1 :(得分: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和{{3它的用法在Chas Emerick,Brian Carper,Christophe Grand等人的Clojure编程中得到了解释,并且在讨论如何获得时也提到了clojurescript来自记录的密钥。因此,由您决定是否使用它是个好主意。