2012-03-08 11 views
5

Sto leggendo un libro per i compiti, e capisco che l'uso di # 'sta trattando la variabile come una funzione anziché come una variabile. Ma sono un po 'confuso su FUNCALL. Capisco che il lisp rende l'oggetto fuori dalle variabili, quindi il nome della funzione è solo un 'puntatore' (potrebbe essere una parolaccia, ma si spera che capisca cosa intendo), nel qual caso si usa # 'per invocarlo, o è funcall l'unico modo per invocarli? ex.Qual è la differenza tra FUNCALL e # 'nome-funzione in comune lisp?

(defun plot (fn min max step) 
(loop for i from min to max by step do 
     (loop repeat (funcall fn i) do (format t "*")) 
     (format t "~%"))) 

non potrei solo fare:

(defun plot (fn min max step) 
(loop for i from min to max by step do 
     (loop repeat #'(fn i) do (format t "*")) 
     (format t "~%"))) 

Credo che la mia confusione sta in ciò che è esattamente nei nomi delle funzioni. Quando ho letto il libro, ha detto che il valore della variabile è ciò che sarà l'oggetto funzione.

risposta

13

#'function-name è (function function-name). Nulla viene chiamato, valutando i risultati nella funzione associata a function-name (l'oggetto che rappresenta la funzione). funcall viene utilizzato per chiamare le funzioni.

Vedere funcall e function nello HyperSpec.

sessione di esempio utilizzando sia:

CL-USER> (defun square (x) (* x x)) 
SQUARE 
CL-USER> #'square 
#<FUNCTION SQUARE> 
CL-USER> (function square) 
#<FUNCTION SQUARE> 
CL-USER> (funcall #'square 3) 
9 
CL-USER> (funcall 'square 3) 
9 

La seconda invocazione del funcall opere perché accetta anche un simbolo come la funzione designatore (vedi il link per funcall sopra per i dettagli).

8

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

  1. quando viene utilizzato come primo elemento di una forma significa una funzione
  2. 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:

lisp-2

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) = &square; ...}) 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.

+4

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

+0

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

+0

@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

Problemi correlati