【问题标题】:merge to set default values, but potentially expensive functions合并以设置默认值,但可能是昂贵的功能
【发布时间】:2015-05-07 21:53:07
【问题描述】:

在 clojure 中设置默认值的惯用方法是使用合并:

;; `merge` can be used to support the setting of default values
(merge {:foo "foo-default" :bar "bar-default"} 
       {:foo "custom-value"})
;;=> {:foo "custom-value" :bar "bar-default"}

然而,实际上,默认值通常不是简单的常量,而是函数调用。显然,如果不使用该函数,我想避免调用它。

到目前为止,我正在做类似的事情:

(defn ensure-uuid [msg]
  (if (:uuid msg)
    msg
    (assoc msg :uuid (random-uuid))))

并应用我的ensure-* 函数,例如(-> msg ensure-uuid ensure-xyz)

有什么更惯用的方法来做到这一点?我在想这样的事情:

(merge-macro {:foo {:bar (expensive-func)} :xyz (other-fn)} my-map)

(associf my-map
  [:foo :bar] (expensive-func)
  :xyz (other-fn))

【问题讨论】:

    标签: clojure


    【解决方案1】:

    您可以将delayforce 结合使用。

    然后您可以合并您的默认值,例如

    (merge {:foo "foo-default" :bar "bar-default" :uuid (delay (random-uuid))}
           {:foo "custom-value" :uuid "abc"})
    

    并使用访问值

    (force (:foo ...))
    

    (force (:uuid ...))
    

    random-uuid 只有在您真正需要该值时才会被调用(并且只是第一次)。

    您可以将对force 的调用封装在get-value 函数中,或类似的东西中。

    【讨论】:

    • 这是一个好主意,但我想避免在整个应用程序代码中调用force。我写了一个非常适合我的用例的宏。干杯和感谢
    • 好。但是,语义不同 - 您的解决方案会评估地图中是否没有提供任何值,延迟它只会评估该值是否最终被使用。
    【解决方案2】:

    我刚刚修改了condp 宏并写了以下内容:

    (defmacro assoc-if-nil
      "Takes a map as the first argument and a succession of key value pairs that
      are used to set the key to value if the key of the map is nil. The value part
      is only evaluated if the key is nil (thus different semantics to (merge)).
      Example:
      (assoc-if-nil {:a {:b :set}}
        [:a :b] :non-def
        [:a :c] :non-def
        :d :non-def)
      ;; =>{:a {:b :set, :c :non-def}, :d :non-def}"
      [m & clauses]
      (assert (even? (count clauses)))
      (let [g (gensym)
            get-fn   (fn[kork] (if (vector? kork) `get-in   `get))
            assoc-fn (fn[kork] (if (vector? kork) `assoc-in `assoc))
            pstep (fn [[kork v]] `(if-not (~(get-fn kork) ~g ~kork)
                                    (~(assoc-fn kork) ~g ~kork ~v)
                                    ~g))]
        `(let [~g ~m ;; avoid double evaluation
               ~@(interleave (repeat g) (map pstep (partition 2 clauses)))]
           ~g)))
    

    扩展为:

    (macroexpand-1 '
    (assoc-if-nil m
                  [:a :b] :nested
                  :d :just-key))
    
    (clojure.core/let
     [G__15391     m
      G__15391
      (clojure.core/if-not
       (clojure.core/get-in G__15391 [:a :b])
       (clojure.core/assoc-in G__15391 [:a :b] :nested)
       G__15391)
      G__15391
      (clojure.core/if-not
       (clojure.core/get G__15391 :d)
       (clojure.core/assoc G__15391 :d :just-key)
       G__15391)]
     G__15391)
    

    【讨论】:

      猜你喜欢
      • 2019-12-10
      • 2023-04-03
      • 1970-01-01
      • 2022-08-06
      • 1970-01-01
      • 2018-01-06
      • 2014-07-25
      • 1970-01-01
      • 2015-02-13
      相关资源
      最近更新 更多