如何在Clojure中为记录中的字段设置默认值?

18

我正在使用Clojure创建记录,并希望为某些字段设置默认值。我该如何做到这一点?

3个回答

23
使用构造函数。
(defrecord Foo [a b c])

(defn make-foo
  [& {:keys [a b c] :or {a 5 c 7}}]
  (Foo. a b c))

(make-foo :b 6)
(make-foo :b 6 :a 8)

当然,有各种各样的变化。例如,您可以要求某些字段为非可选且没有默认值。

(defn make-foo
  [b & {:keys [a c] :or {a 5 c 7}}]
  (Foo. a b c))

(make-foo 6)
(make-foo 6 :a 8)

因人而异。


这是一个链接,解释了在Clojure中如何使用*:keys:or*关键字参数。http://stuartsierra.com/2010/01/15/keyword-arguments-in-clojure - leeor
我们也可以像这样使用 map->Foo 和/或 ->Foo(defn make-foo [& args] (map->Foo (into args {:a 5 :b 1 :c 7})) (make-foo {:b 3}) - 这样可以节省一些解构的代码。 - birdspider

14

当你通过一个扩展映射构造记录时,可以很容易地传递初始值:

(defrecord Foo [])

(def foo (Foo. nil {:bar 1 :baz 2}))

因此,我通常创建一个构造函数来合并一些默认值(您可以根据需要进行覆盖):

(defn make-foo [values-map]
  (let [default-values {:bar 1 :baz 2}]
    (Foo. nil (merge default-values values-map))))

(make-foo {:fiz 3 :bar 8})
=> #:user.Foo{:fiz 3, :bar 8, :baz 2}

2

在遇到同样的问题后,我最终使用宏将defrecord和工厂函数包装为单个定义。

该宏:

(defmacro make-model
  [name args & body]
  (let [defaults (if (map? (first body)) (first body) {})
        constructor-name (str/lower-case (str "make-" name))]
    `(do (defrecord ~name ~args ~@(if (map? (first body)) (rest body) body))
         (defn ~(symbol constructor-name)
           ([] (~(symbol constructor-name) {}))
           ([values#] (~(symbol (str "map->" name)) (merge ~defaults values#)))))))

使用方法

(make-model User [firstName lastName] {:lastName "Smith"})
=> #'user/make-user
(make-user {:firstName "John"})
=> #user.User{:firstName "John", :lastName "Smith"}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接