2013-10-17 14 views
6

Troviamo questo generatore di funzioni per realizzare la composizione in "ANSI Common Lisp" di P.Graham (pagina 110). Gli argomenti sono n> 0 nomi di funzioni quotate. Non capisco completamente, quindi mi cito il codice qui e specificare le mie domande sotto di esso:(comporre) in Common Lisp

(defun compose (&rest fns) 
    (destructuring-bind (fn1 . rest) (reverse fns) 
    #'(lambda (&rest args) 
     (reduce #'(lambda (v f) (funcall f v)) 
       rest 
       :initial-value (apply fn1 args))))) 

La lista degli argomenti di comporre si inverte e scompattato, la sua (ora primo) elemento legato a 'fn1 'e il resto a' riposo '. Il corpo del lambda più esterno è un riduttore: (funcall fi (funcall fi-1 ...)), con gli operandi in ordine invertito per ripristinare quello iniziale.

1) Qual è il ruolo dell'espressione lambda più esterna? Vale a dire, da dove vengono le sue "args"? La struttura dei dati è specificata come il primo argomento di destructuring-bind? 2) Da dove viene il primo lambda a prendere i suoi due argomenti?

Voglio dire che posso apprezzare ciò che fa il codice, ma ancora lo scope lessicale è un po 'un mistero per me. In attesa di tutti i commenti! // in anticipo // Marco

+0

Perché tutte le staffe pensili? – Marcin

+1

Le mie scuse, non ho visto la discussione al riguardo su http://stackoverflow.com/questions/5928106/compose-example-in-paul-grahams-ansi-common-lisp – ocramz

+0

@Marcin, per guidare l'occhio;) è una cattiva pratica? Sono ancora un noob – ocramz

risposta

10

E 'probabilmente più facile se si considera prima un paio di esempi pratici:

(defun compose1 (a) 
    (lambda (&rest args) 
    (apply a args))) 

(defun compose2 (a b) 
    (lambda (&rest args) 
    (funcall a (apply b args)))) 

(defun compose3 (a b c) 
    (lambda (&rest args) 
    (funcall a (funcall b (apply c args))))) 

Così il più esterno lambda è il valore di ritorno: una funzione che prende alcun argomento, quello che fa con esso si applica l'ultima funzione e concatenazione di tutti gli altri in ordine inverso sul risultato ottenuto dall'ultima funzione.

Nota: compose1 potrebbe essere definito più semplicemente come (defun compose1 (a) a).

una versione un po equivalenti ma meno efficiente potrebbe essere

(defun compose (&rest functions) 
    (if (= (length functions) 1) 
     (car functions) 
     (lambda (&rest args) 
     (funcall (first functions) 
       (apply (apply #'compose (rest functions)) 
         args))))) 
+0

Grazie per gli esempi e la spiegazione, questo sicuramente chiarisce le cose! – ocramz

2

1) Il lambda più esterno crea una chiusura per te, perché il risultato di (combinare ...) è una funzione che calula la composizione di altre funzioni.

2) L'argomento lambda più interno ottiene l'argomento dalla funzione reduce. Riduci prende una funzione (la più interna lambda) di due argomenti e la applica gradualmente ad una lista, ad es.

(reduce #'- '(1 2 3 4)) is (- (- (- 1 2) 3) 4)