I #'
e funcall
notazioni sono necessari in Common Lisp, perché questo linguaggio è un cosiddetto "Lisp-2", in cui un determinato simbolo può avere due principali "significati" separati e non collegati normalmente indicati come
- quando viene utilizzato come primo elemento di una forma significa una funzione
- quando utilizzato in qualsiasi altro luogo significa una variabile
Queste sono spiegazioni approssimative, come si vedrà nel seguente esempio che "prima el ement di un modulo "e" qualsiasi altro luogo "non sono definizioni corrette.
Si consideri ad esempio:
stampe il codice sopra 144
... può sembrare sorprendente in un primo momento, ma il motivo è che lo stesso nome square
viene utilizzato con due diversi significati: la funzione che dato un argomento restituisce il risultato della moltiplicazione l'argomento stesso e il quadrato variabile locale con valore 12.
il primo e il terzo usi del nome square
il significato è la funzione chiamato square
e ho dipinto il nome con il colore rosso. Il secondo e il quarto utilizzo riguardano invece una variabile denominata square
e sono invece verniciate in blu.
In che modo Common Lisp può decidere quale è quale? il punto è la posizione ...dopo defun
è chiaramente in questo caso un nome di funzione, come se fosse un nome di funzione nella prima parte di (square square)
. Allo stesso modo come primo elemento di una lista all'interno di un modulo let
, è chiaramente un nome di variabile ed è anche un nome di variabile nella seconda parte di (square square)
.
Questo sembra piuttosto psicotico ... non è vero? Beh, c'è un po 'di suddivisione nella comunità Lisp su se questo doppio significato renderà le cose più semplici o complesse ed è una delle principali differenze tra Common Lisp e Scheme.
Senza entrare nei dettagli mi limiterò a dire che questa scelta apparentemente folle è stato fatto per rendere le macro Lisp più utile, fornendo abbastanza igiene per farli lavorare bene, senza la complessità e la potenza espressiva tolto di macro igieniche pieno . Sicuramente è una complicazione che rende più difficile spiegare la lingua a chiunque la stia imparando (ed è per questo che Scheme è considerato un linguaggio migliore per insegnare) ma molti lispers esperti pensano che sia una buona scelta che rende il linguaggio Lisp un strumento migliore per problemi reali.
Anche nei linguaggi umani il contesto gioca comunque un ruolo importante e non c'è un problema serio per gli esseri umani che a volte la stessa parola possa essere usata con significati diversi (ad esempio come un sostantivo o come un verbo come "California è il Stato in cui vivo "o" Dichiara la tua opinione ").
Anche in un Lisp-2 è necessario tuttavia utilizzare le funzioni come valori, ad esempio passandole come parametri o memorizzandole in una struttura dati oppure è necessario utilizzare valori come funzioni, ad esempio chiamando una funzione che è stata ricevuto come parametro (il tuo caso trama) o che è stato memorizzato da qualche parte. Questo è dove #'
e funcall
entrano in gioco ...
#'foo
è davvero solo una scorciatoia per (function foo)
, esattamente come 'x
è una scorciatoia per (quote x)
. Questa cosa "funzione" è una forma speciale che, dato un nome (foo
in questo caso) restituisce la funzione associata come un valore, che è possibile memorizzare nelle variabili o passare intorno:
(defvar *fn* #'square)
nel codice sopra per esempio la variabile *fn*
riceverà la funzione definita in precedenza. Un valore di funzione può essere manipolato come qualsiasi altro valore come una stringa o un numero.
funcall
è l'opposto, permettendo di chiamare una funzione non utilizzando il suo nome ma utilizzando un valore ...
(print (funcall *fn* 12))
il codice sopra visualizzerà 144 ... perché la funzione che è stata memorizzata nella la variabile *fn*
ora viene chiamata passare 12 come argomento.
Se si conosce il "C" linguaggio di programmazione un'analogia sta valutando (let ((p #'square))...)
come prendere l'indirizzo della funzione square
(come con { int (*p)(int) = □ ...}
) e invece (funcall p 12)
è come chiamare una funzione tramite un puntatore (come con (*p)(12)
che "C" consente di essere abbreviato in p(12)
).
La parte confusionaria in Common Lisp è che è possibile avere sia una funzione denominata square
sia una variabile denominata square
nello stesso ambito e la variabile non nasconde la funzione. funcall
e function
sono due strumenti che è possibile utilizzare quando è necessario utilizzare il valore di una variabile come funzione o quando si desidera una funzione come valore, rispettivamente.
Penso che la tua spiegazione potrebbe essere meno «mistica» se hai usato la parola 'namespace' invece della vaga parola' significato'. CL è un Lisp-2 che significa che un simbolo può apparire in entrambi gli spazi dei nomi con un valore diverso in ciascuno. cioè il simbolo 'pippo' può avere il valore 42 nel valore spazio dei nomi (puoi guardare il suo valore con' valore-simbolo') e può anche avere il valore di '(lambda (x) (1+ x))' nel namespace della funzione ('symbol-function'). – Daimrod
Grazie per quello. Mi chiedo. Nel tuo caso, hai passato la funzione in una variabile globale. Se hai la funzione lì dentro, non potresti anche fare (\ * fn \ * 12)? o è solo per la funzione quadrata, che esiste come funzione? – Andy
@Andy: la forma '(* fn * 12)' significa in comune lisp "richiama la funzione che si chiama' * fn * 'passaggio 12 come parametro" ... la funzione chiamata è fissata logicamente e potrebbe anche essere inline dal compilatore: è la funzione '(defun * fn * (x) ...)'. La variabile nominata allo stesso modo è un'altra cosa non correlata e può contenere qualsiasi valore ... ad esempio un numero, una stringa o anche l'indirizzo di una funzione. La forma '(funcall * fn * 12)' significa "chiama la funzione il cui indirizzo è contenuto nella variabile chiamata' * fn * '". D'altra parte il '# 'quadrato' significa, più o meno," l'indirizzo della funzione 'quadrato' ". – 6502