2012-01-08 14 views
8

Sto solo imparando ANSI Common Lisp (usando il clisp su una macchina Win32) e mi chiedevo se mapcar potesse usare una funzione passata come argomento formale? Vedere il seguente:Programmazione di ordine superiore con Lisp: passaggio di una funzione a mapcar?

(defun foo (fn seq) 
    (mapcar #'fn seq)) 

Questo sarebbe, a mio parere, di fornire un maggiore livello di flessibilità rispetto:

(defun mult (i) 
    (* i 2)) 

(defun foo() 
    (mapcar #'mult '(1 2 3))) 

risposta

3

Certo, si può fare questo:

(defun foo (fn) 
    (mapcar fn '(1 2 3))) 

Esempi:

(foo #'(lambda (x) (* x 2))) 
(foo #'1+) 
(foo #'sqrt) 
(foo #'(lambda (x) (1+ (* x 3)))) 
28

Questo è in abs assolutamente possibile! Ci sei quasi. Ti sei imbattuto nei doppi spazi dei nomi di Common Lisp, che possono richiedere molto tempo per abituarti. Spero di poter dire una cosa o due per rendere i due spazi dei nomi di Common Lisp un po 'meno confusi.

Il codice è quasi corretto. Hai scritto:

(defun foo (fn seq) 
    (mapcar #'fn seq)) 

Ma, che cosa sta cercando di fare? Bene, # ' è una scorciatoia. Lo espanderò per te.

(defun foo (fn seq) 
    (mapcar (function fn) seq)) 

Quindi, # 'simbolo è una scorciatoia per (funzione simbolo). In Common Lisp - come sembra che tu sappia - i simboli possono essere associati a una funzione ea una variabile; questi sono i due namespace di cui i Lispers parlano tanto: lo spazio dei nomi delle funzioni e lo spazio dei nomi delle variabili.

Ora, ciò che la funzione fa è ottenere la funzione associata a un simbolo o, se lo si desidera, il valore che il simbolo ha nel namespace della funzione.

Ora, sulla REPL, quello che hai scritto sarebbe ovviamente quello che vuoi.

(mapcar #'car sequence) 

Sarebbe mappare la funzione auto ad una sequenza di liste. Il simbolo auto non ha alcuna associazione vincolante, solo una funzione vincolante, motivo per cui è necessario utilizzare (funzione ...) (o la sua stenografia, # ') per ottenere la funzione effettiva.

tuo funzione foo non funziona perché la funzione si passa come argomento viene legato ad un simbolo come variabile. Prova questo:

(let ((fn #'sqrt)) 
    (mapcar #'fn '(4 9 16 25))) 

Potrebbe esserti aspettato un elenco di tutte le radici quadrate di quei numeri, ma non ha funzionato. Questo perché hai utilizzato per consentire di associare la funzione radice quadrata a fn come variabile. Ora provate questo codice:

(let ((fn #'sqrt)) 
    (mapcar fn '(4 9 16 25))) 

Delizioso!Ciò lega la funzione radice quadrata al simbolo fn come variabile.

Quindi, andiamo rivedere il tuo foo funzione:

(defun foo (fn seq) 
    (mapcar fn seq)) 

Che volontà lavoro, perché fn è una variabile. Testiamo esso, solo per assicurarsi che:

;; This will not work 
(foo sqrt '(4 9 16 25)) 
;; This will work 
(foo #'sqrt '(4 9 16 25)) 

Il primo non ha funzionato, perché la funzione radice quadrata è destinata a sqrt nella funzione dello spazio dei nomi. Quindi, nella seconda, abbiamo afferrato la funzione dal simbolo e l'abbiamo passata a foo, che lo legava al simbolo fn come variabile.


OK, quindi cosa succede se si desidera associare una funzione a un simbolo nel namespace della funzione? Bene, per i principianti, defun lo fa, in modo permanente. Se vuoi che sia temporaneo, come un vincolante, usa flet. Flet è, a mio avviso, stupido, perché non funziona esattamente come fa. Ma, darò un esempio, solo così puoi vedere.

(flet ((fn (x) (sqrt x))) 
    (mapcar fn '(4 9 16 25))) 

non funzionerà, perché flet non lega la funzione al simbolo nello spazio dei nomi variabile, ma nella funzione dello spazio dei nomi.

(flet ((fn (x) (sqrt x))) 
    (mapcar #'fn '(4 9 16 25))) 

Questo farà quello che ci si aspetta, perché flet legato che funzioni sul simbolo fn nella funzione dello spazio dei nomi. E, giusto per guidare l'idea della casa funzione di spazio dei nomi:

(flet ((fn (x) (sqrt x))) 
    (fn 16)) 

tornerà 4.

+0

1 Wow, che è un po 'di risposta. Molto dettagliato. :-) –

Problemi correlati