6

我有一个条件,例如:

(cond
  (condition1) (consequent1)
  (condition2) (consequent2))

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

(let [value-used-in-cond2 do-expensive-computation]
  (cond
  (condition1) (consequent1)
  (condition2) (consequent2)))

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

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

4

6 回答 6

8

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

于 2013-04-02T21:10:30.713 回答
4

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

(let [expensive-result (or (condition1) (do-expensive-computation)] 
   (cond (condition1) (consequent1)
         (condition2) (consequent2)))

然而,这需要对 condition1 进行两次评估。

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

(let (var)
   (cond
      ((condition1) (consequent1))
      ((setq var (condition2)) (consequent2))))

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

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

(let [v (atom nil)]
  (cond
    (condition1) (consequent1)
    (do (reset! v (expensive)) (condition2 @v)) (consequent2 @v)))
于 2013-04-02T19:36:13.483 回答
3

使用 adelay最多计算一次并使用它零次或多次:

(let [expensive-thing (delay do-expensive-computation)]
  (cond (condition1) (consequent1)
        (condition2 @expensive-thing) (consequent2 @expensive-thing)))
于 2013-04-03T06:34:07.123 回答
2

在 Clojure 中重写它以避免重复计算的一种方法是:

(or
  (when (condition1) (consequent1))
  (when-let [val2 (condition2)] (consequent2 val2)))

这在假设consequent1并且consequent2永远不会返回的情况下起作用nil- 否则对 的评估or将落入下一个形式。

于 2013-04-02T22:07:24.730 回答
0

I had a similar problem, but I only have two case condition so I used a combination of a function and the if-let macro so:

(defn- expensive-computation
  [a b]
  (if (test (compute a b)) a nil))

(if-let [foo (expensive-computation a b)]
  (consequent2 foo)
  (consequent1))

As you can see the value is only computed once, after that a simple comparison is done to check if the test of the computation was sucessfull or not. If the test was not sucessful then it return nil, thus executing consequent1. It is not a super clean solution but it was the best that I found. Hope it helps

于 2015-12-08T15:28:41.920 回答
0

如果条件 2 使用了一个昂贵的值,但条件 2 是假的,我假设它的形式是

(and (expensive-calculation) (other-extra-condition))

在这些情况下,我所做的是:

(let (expensive-value)
  (cond (condition1 consequent1)
        (and (setq expensive-value (expensive-calculation)) (other-extra-condition)) consequent2)
        ((and expensive-value (other-extra-condition2)) consequent3)
于 2015-12-08T17:29:42.393 回答