2013-04-02 13 views
6

Mam dyr np formularza:Ponowne użycie wartości w warunkach iw konsekwencji dla efektywności w lisp/Clojure

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

mówią w warunek2 chcę obliczyć pewną wartość, która jest drogie, więc wolałbym Zrób to tylko raz. Jeśli warunek2 jest prawdziwy, chciałbym użyć tej kosztownej wartości w consequent2. Moim dylematem jest więc to, że nie chcę przeliczać wartości warunku i konsekwencji, ponieważ jest to marnotrawstwem. Ja też nie chcę rzucić całego skoku wewnątrz większej funkcji let, np.

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

ponieważ nie chcę, aby obliczyć tę wartość, jeśli nie mogę dostać się do stanu 2, to znaczy jeśli condition1 jest prawdą.

Czy istnieje idiomatyczny sposób radzenia sobie z tym? Pierwszą rzeczą, która przychodzi na myśl, jest zapamiętanie kosztownej funkcji, ale musi istnieć prostsze rozwiązanie.

+0

Weź spójrz na 'condp' i jego użycie': >> '. –

+0

To może być również modelowane przy użyciu leniwej semantyki oceny – Ankur

Odpowiedz

4

Nieco brzydki rozwiązanie, które powinno działać w Clojure jest

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

Wymaga to jednak warunek1 być oceniane dwukrotnie.

Zakładając, że lisp/Clojure w nagłówku oznacza Clojure lub (inny) LISP, w Common Lisp można zrobić

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

ale to nie będzie działać w Clojure w zmiennej lokalnej jest niezmienna.

Możesz użyć atomu, aby osiągnąć coś podobnego w Clojure.

(let [v (atom nil)] 
    (cond 
    (condition1) (consequent1) 
    (do (reset! v (expensive)) (condition2 @v)) (consequent2 @v))) 
+0

Możesz zrobić to samo z atomami w Clojure. – Jeremy

8

W Na Lisp, Paul Graham opisuje makra dla anaforycznego wariantów Common Lisp warunkowych, które wiążą się z symbolu „IT z wartością wyrażenia stan. Makra te stosują tę samą semantykę oceny co zwykłe formularze warunkowe, więc z twojego przykładu, condition2 będą oceniane po condition1 i tylko jeśli condition1 jest fałszywe. Wszystkie warunki zostaną ocenione najwyżej jeden raz. Można pobrać Na Lisp na http://www.paulgraham.com/onlisptext.html, a kod do anaforycznego makr jest na rysunku 14.1 na stronie 191.

+1

I nieco bardziej bezpośrednio, sprawdź akordowe makro Grahama. –

2

Jednym ze sposobów poprawiania to w Clojure, aby uniknąć powtarzania obliczeń byłoby:

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

Działa to przy założeniu, że consequent1 i consequent2 nigdy nie zwrócą nil - w przeciwnym razie ocena or spadnie do następnego formularza.

3

użyć delay do obliczenia czegoś co najwyżej raz i używać go zero lub więcej razy:

(let [expensive-thing (delay do-expensive-computation)] 
    (cond (condition1) (consequent1) 
     (condition2 @expensive-thing) (consequent2 @expensive-thing))) 
0

miałem podobny problem, ale mam tylko dwa przypadek warunek więc użyłem kombinacji funkcji i if-let makro tak:

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

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

Jak widać wartość jest obliczana tylko raz, potem proste porównanie odbywa się sprawdzenie, czy test obliczeń było sucessfull czy nie. Jeśli test nie zakończył się sukcesem, to zwraca zero, w ten sposób wykonując consequent1. To nie jest super czyste rozwiązanie, ale to było najlepsze, jakie znalazłem. Nadzieję, że pomoże

0

Jeśli warunek2 używa drogich wartość, a jeszcze warunek2 jest fałszywa, zakładam, że ma postać

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

Co zrobić w tych przypadkach jest:

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