在条件中重用值,从而提高lisp/clojure的效率

zen*_*nna 6 lisp performance conditional clojure

我有一个cond,例如以下形式:

(cond
  (condition1) (consequent1)
  (condition2) (consequent2))
Run Code Online (Sandbox Code Playgroud)

在条件2中说我想计算一些价格昂贵的价值,所以我宁愿只做一次.如果condition2为true,那么我想在consequent2中使用这个昂贵的值.我的困境是,我不想重新计算条件和结果中的价值,因为这是浪费.我也不想把整个cond扔进一个更大的let函数中,例如

(let [value-used-in-cond2 do-expensive-computation]
  (cond
  (condition1) (consequent1)
  (condition2) (consequent2)))
Run Code Online (Sandbox Code Playgroud)

因为如果我从未达到条件2,即如果condition1为真,我不想计算这个值.

有没有惯用的方法来解决这个问题?首先想到的是记住昂贵的功能,但必须有更简单的解决方案.

Bri*_*n B 8

On Lisp中,Paul Graham描述了Common Lisp条件的照应变体的宏,它将符号'it'绑定到条件表达式的值.这些宏遵循与普通条件形式相同的评估语义,因此从您的示例开始,condition2condition1仅在if condition1为false时进行评估.所有条件最多只评估一次.您可以在http://www.paulgraham.com/onlisptext.html下载On Lisp,而照应宏的代码在第191页的图14.1中.


Ter*_* D. 4

一个应该在 Clojure 中工作的有点丑陋的解决方案是

(let [expensive-result (or (condition1) (do-expensive-computation)] 
   (cond (condition1) (consequent1)
         (condition2) (consequent2)))
Run Code Online (Sandbox Code Playgroud)

然而,这需要对条件 1 进行两次评估。

假设标题中的 lisp / clojure 表示 Clojure或(另一个)lisp,在 Common Lisp 中你可以这样做

(let (var)
   (cond
      ((condition1) (consequent1))
      ((setq var (condition2)) (consequent2))))
Run Code Online (Sandbox Code Playgroud)

但这在 Clojure 中不起作用,因为局部变量是不可变的。

您可以使用原子来完成与 Clojure 类似的事情。

(let [v (atom nil)]
  (cond
    (condition1) (consequent1)
    (do (reset! v (expensive)) (condition2 @v)) (consequent2 @v)))
Run Code Online (Sandbox Code Playgroud)