cond->有多个值

Maa*_*ens 3 clojure

我遇到了很多情况,如果满足某个条件,则需要"更新"两个(或甚至三个)值的向量,否则单独使用.例:

(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->.

Ala*_*son 6

为此,我们可以复制在图形处理和其他用例中常见的技巧,其中函数总是接受上下文映射作为第一个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] ...如果你经常使用它,你可能会写一个像删除一些样板的宏.