6

たとえば、次のような形式の条件があります。

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

condition2 で、高価な値を計算したいので、一度だけ実行したいと思います。condition2 が true の場合、consequent2 でこの高価な値を使用したいと思います。私のジレンマは、条件と結果の値を再計算したくないということです。これは無駄だからです。また、より大きな let 関数内に cond 全体をスローしたくありません。

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

条件 2 に到達しない場合、つまり condition1 が true の場合は、この値を計算したくないためです。

これに対処する慣用的な方法はありますか?最初に頭に浮かぶのは、高価な関数をメモすることですが、もっと簡単な解決策があるはずです。

4

6 に答える 6

8

On Lispで、Paul Grahamは、条件式の値にシンボル 'it をバインドする Common Lisp 条件のアナフォリック バリアントのマクロについて説明しています。これらのマクロは、通常の条件付きフォームと同じ評価セマンティクスに従うため、例から、false の場合にのみcondition2評価されます。すべての条件は、最大 1 回評価されます。On Lisphttp://www.paulgraham.com/onlisptext.htmlからダウンロードできます。アナフォリック マクロのコードは、191 ページの図 14.1 にあります。condition1condition1

于 2013-04-02T21:10:30.713 に答える
4

Clojureで機能するはずのやや醜い解決策は次のとおりです

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

ただし、これには condition1 を 2 回評価する必要があります。

見出しの 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 に答える
2

計算を繰り返さないように Clojure でこれを書き直す 1 つの方法は次のとおりです。

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

これは、それを前提として機能しconsequent1consequent2決して返されません。nilそうでない場合、の評価はor次のフォームにフォールスルーします。

于 2013-04-02T22:07:24.730 に答える