Blog coding and discussion of coding about JavaScript, PHP, CGI, general web building etc.

Tuesday, December 8, 2015

Reusing values in condition and consequent for efficiency in lisp / clojure

Reusing values in condition and consequent for efficiency in lisp / clojure


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.

Answer by Terje D. for Reusing values in condition and consequent for efficiency in lisp / clojure


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)))  

Answer by Brian B for Reusing values in condition and consequent for efficiency in lisp / clojure


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.

Answer by Alex for Reusing values in condition and consequent for efficiency in lisp / clojure


One way of rewriting this in Clojure to avoid repeating the computation would be:

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

This works assuming that consequent1 and consequent2 never return nil - otherwise evaluation of the or would fall through to the next form.

Answer by amalloy for Reusing values in condition and consequent for efficiency in lisp / clojure


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)))  

Answer by carocad for Reusing values in condition and consequent for efficiency in lisp / clojure


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

Answer by Leo for Reusing values in condition and consequent for efficiency in lisp / clojure


If condition2 uses an expensive value, and yet condition2 is false, I assume that it is of the form

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

What I do in these cases is:

(let (expensive-value)    (cond (condition1 consequent1)          (and (setq expensive-value (expensive-calculation)) (other-extra-condition)) consequent2)          ((and expensive-value (other-extra-condition2)) consequent3)  


Fatal error: Call to a member function getElementsByTagName() on a non-object in D:\XAMPP INSTALLASTION\xampp\htdocs\endunpratama9i\www-stackoverflow-info-proses.php on line 71

0 comments:

Post a Comment

Popular Posts

Powered by Blogger.