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为真,我不想计算这个值.
有没有惯用的方法来解决这个问题?首先想到的是记住昂贵的功能,但必须有更简单的解决方案.
在On Lisp中,Paul Graham描述了Common Lisp条件的照应变体的宏,它将符号'it'绑定到条件表达式的值.这些宏遵循与普通条件形式相同的评估语义,因此从您的示例开始,condition2将condition1仅在if condition1为false时进行评估.所有条件最多只评估一次.您可以在http://www.paulgraham.com/onlisptext.html下载On Lisp,而照应宏的代码在第191页的图14.1中.
一个应该在 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)