I have a cond, e.g of the form:
(cond
(condition1) (consequent1)
(condition2) (consequent2))
Say in condition2 I want to compute some value which is expensive, so I would prefer to only do it once. If condition2 is true then I would like to use this expensive value in consequent2. My dilemma is then that I don't want to recompute the value in the condition and consequent, since this is wasteful. I also don't want to throw the whole cond inside a larger let function, e.g.
(let [value-used-in-cond2 do-expensive-computation]
(cond
(condition1) (consequent1)
(condition2) (consequent2)))
since I don't want to compute this value if I never get to condition 2, i.e. if condition1 is true.
Is there an idiomatic way to deal with this? The first thing that comes to mind is to memoizing the expensive function, but there must be simpler solution.
In On Lisp, Paul Graham describes macros for anaphoric variants of Common Lisp conditionals which bind the symbol 'it to the value of the condition expression. These macros obey the same evaluation semantics as the normal conditional forms, so from your example, condition2
will be evaluated after condition1
and only if condition1
is false. All conditions will be evaluated at most once. You can download On Lisp at http://www.paulgraham.com/onlisptext.html, and the code for the anaphoric macros is in figure 14.1 on page 191.
A somewhat ugly solution that should work in Clojure is
(let [expensive-result (or (condition1) (do-expensive-computation)]
(cond (condition1) (consequent1)
(condition2) (consequent2)))
This however requires condition1 to be evaluated twice.
Assuming that lisp / clojure in the heading means Clojure or (another) lisp, in Common Lisp you can do
(let (var)
(cond
((condition1) (consequent1))
((setq var (condition2)) (consequent2))))
but this will not work in Clojure at the local variable is immutable.
You can use an atom to accomplish something similar with Clojure.
(let [v (atom nil)]
(cond
(condition1) (consequent1)
(do (reset! v (expensive)) (condition2 @v)) (consequent2 @v)))
Use a delay
to compute something at most once and use it zero or more times:
(let [expensive-thing (delay do-expensive-computation)]
(cond (condition1) (consequent1)
(condition2 @expensive-thing) (consequent2 @expensive-thing)))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With