2012-10-18 19 views
7

Sono confuso su come def e lasciare variabili di binding in modo diverso. Qualcuno può spiegarmi perché questo funziona:Funzione di ricorrenza interna let

(def leven 
    (memoize (fn [x y] 
    (cond (empty? x) (count y) 
     (empty? y) (count x) 
     :else (min (+ (leven (rest x) y) 1) 
        (+ (leven x (rest y)) 1) 
        (+ (leven (rest x) (rest y)) (if (= (first x) (first y)) 0 1)) 
      ) 
))) 
) 

Ma quando provo a dichiarare la funzione come lasciarlo fallisce la compilazione:

(def leven 
    (let [l (memoize (fn [x y] 
    (cond (empty? x) (count y) 
      (empty? y) (count x) 
      :else (min (+ (l (rest x) y) 1) 
         (+ (l x (rest y)) 1) 
         (+ (l (rest x) (rest y)) (if (= (first x) (first y)) 0 1)) 
       ) 
    ) 
    ))] 
    (l x y) 
    ) 
) 

EDIT: Questo funziona, utilizzando la tecnica ha mostrato da Ankur.

(defn leven [x y] 
(let [l (memoize (fn [f x y] 
(cond (empty? x) (count y) 
     (empty? y) (count x) 
     :else (min (+ (f f (rest x) y) 1) 
        (+ (f f x (rest y)) 1) 
        (+ (f f (rest x) (rest y)) (if (= (first x) (first y)) 0 1)) 
      ) 
) 
)) 
magic (partial l l)] 
(magic x y) 
) 
) 

risposta

7

Di seguito è riportato un esempio di ciò che è stato richiesto. Sto usando fattoriale solo per il gusto di semplicità e ha aggiunto println in fattoriale per assicurarsi che il Memoizzazione sta lavorando bene

(let [fact (memoize (fn [f x] 
         (println (str "Called for " x)) 
         (if (<= x 1) 1 (* x (f f (- x 1)))))) 
     magic (partial fact fact)] 
    (magic 10) 
    (magic 11)) 

Prima calcolare fattoriale di 10 e poi 11, nel qual caso non dovrebbe ancora chiamare fattoriale per 10 fino a 1 come è stato memoized.

Called for 10 
Called for 9 
Called for 8 
Called for 7 
Called for 6 
Called for 5 
Called for 4 
Called for 3 
Called for 2 
Called for 1 
Called for 11 
39916800 
+0

Molto interessante. Quindi in pratica stai solo passando la funzione come argomento in modo che il compilatore non si confonda su di esso non essendo definito. Non posso provarlo ora, ma ho intenzione di provare questo approccio più tardi. – onit

6

La forma let lega nomi in sequenza in modo da nella definizione seconda funzione il nome l non esiste quando si tenta di fare riferimento ad esso. È possibile utilizzare letfn (con alcune mods minori) o dare la funzione definita un nome e invece fare riferimento a che, invece, in questo modo:

(def leven 
    (let [l (memoize (fn SOME-NAME [x y] 
    (cond 
     (empty? x) (count y) 
     (empty? y) (count x) 
     :else (min (+ (SOME-NAME (rest x) y) 1) 
       (+ (SOME-NAME x (rest y)) 1) 
       (+ (SOME-NAME (rest x) (rest y)) (if (= (first x) (first y)) 0 1))))))] 
l)) 

Come si può notare cambio il ritorno dalla let essere l sé dal momento che è ciò che si desidera leven associato a. Il (l x y) era problematico perché faceva riferimento a collegamenti solo locali alla funzione e non accessibili allo let.

+2

La funzione SOME-NAME non perde i vantaggi del memoize se utilizzata in questo modo? Non è necessario chiamare la funzione memoize return o non è possibile avere una funzione ricorsiva memoized in una dichiarazione let? – onit

+1

@onit La definizione 'leven' può essere modificata per ottenere i benefici della memoizzazione spostando' SOME-NAME' come primo argomento: '(fn [SOME-NAME xy]', e quindi anche sostituendo le chiamate a 'SOME -NOME' a '(SOME-NOME SOME-NAME ...)' e infine sostituendo il valore restituito 'l' per essere' (partial ll) '. –