我遇到了很多情况,如果满足某个条件,则需要"更新"两个(或甚至三个)值的向量,否则单独使用.例:
(let [val1 some-value
val2 some-other-value
[val1, val2] (if something-true
(first-calculation val1 val2 some-other-arg)
[val1, val2])
[val1, val2] (if something-else-true
(second-calculation some-other-arg val1 val2)
[val1, val2])
...etc...)
Run Code Online (Sandbox Code Playgroud)
其中假设第一次计算和第二次计算返回带有可能更新值的向量[val1,val2].
这种代码风格不仅笨重,而且每次都可能由于向量创建和解构而产生一些不必要的开销.
有没有人建议如何使用vanilla Clojure改进此代码,而不创建宏?换句话说,我正在为多个值寻找一种cond->.
为此,我们可以复制在图形处理和其他用例中常见的技巧,其中函数总是接受上下文映射作为第一个arg(或者,在我们的例子中,是上下文向量).尝试重写它,如下所示.请注意args中的更改为second-calculation:
(defn first-calculation
[ctx ; first arg is the context (vec here, usually a map)
some-other-arg]
(let [[val1 val2] ctx] ; destructure the context into locals
...
[val1-new val2-new] )) ; return new context
(defn second-calculation
[ctx ; first arg is the context (vec here, usually a map)
some-other-arg]
(let [[val1 val2] ctx] ; destructure the context into locals
...
[val1-new val2-new] )) ; return new context
(let [ctx [some-value some-other-value]
(cond-> ctx
something-true (first-calculation some-other-arg)
something-else-true (second-calculation some-other-arg)
...etc... ))
Run Code Online (Sandbox Code Playgroud)
这是一个更具体的例子:
(defn inc-foo [ctx amount]
(let [{:keys [foo bar]} ctx
foo-new (+ foo amount)
ctx-new (assoc ctx :foo foo-new)]
ctx-new ))
(defn inc-bar [ctx amount]
(let [{:keys [foo bar]} ctx
bar-new (+ bar amount)
ctx-new (assoc ctx :bar bar-new)]
ctx-new ))
(dotest
(loop [i 0
ctx {:foo 0 :bar 0}]
(let [{:keys [foo bar]} ctx
>> (println (format "i =%2d foo =%3d bar =%3d " i foo bar))
ctx-new (cond-> ctx
(zero? (mod i 2)) (inc-foo i)
(zero? (mod i 3)) (inc-bar i))]
(if (< 9 i)
ctx-new
(recur (inc i) ctx-new)))))
Run Code Online (Sandbox Code Playgroud)
结果:
i = 0 foo = 0 bar = 0
i = 1 foo = 0 bar = 0
i = 2 foo = 0 bar = 0
i = 3 foo = 2 bar = 0
i = 4 foo = 2 bar = 3
i = 5 foo = 6 bar = 3
i = 6 foo = 6 bar = 3
i = 7 foo = 12 bar = 9
i = 8 foo = 12 bar = 9
i = 9 foo = 20 bar = 9
i =10 foo = 20 bar = 18
Run Code Online (Sandbox Code Playgroud)
(with-context [foo bar] ...如果你经常使用它,你可能会写一个像删除一些样板的宏.