2012-12-21 15 views
9

C'è qualcosa che non riesco a capire su Common Lisp.Simboli simboli non integrati

Si supponga che sto scrivendo una macro simile a questo:

(defmacro test-macro() 
    (let ((result (gensym))) 
     `(let ((,result 1)) 
     (print (incf ,result))))) 

di quello che posso fare

> (test-macro) 
2 
2 

Ora voglio vedere come si espande

> (macroexpand-1 '(test-macro)) 
(LET ((#:G4315 1)) (PRINT (INCF #:G4315))) ; 
T 

Ok. Ci sono simboli unici generati con gensym che sono stati stampati come non integrati.

Quindi, per quanto ne so, i simboli non integrati sono i simboli per i quali il valutatore non crea vincoli di dati-simbolo internamente.

Quindi, se si espande la macro in quel modulo, dovrebbe esserci un errore su (incf #: G4315). Per verificare questa possiamo solo valutare quella forma nella REPL:

> (LET ((#:G4315 1)) (PRINT (INCF #:G4315))) 
*** - SETQ: variable #:G4315 has no value 

Allora perché la macro che si espande per questa stringa funziona perfettamente e la forma in sé non lo fa?

risposta

16

I simboli possono essere internati in un pacchetto oppure no. Un simbolo internato in un pacchetto può essere cercato e trovato. Un simbolo senza indirizzo non può essere cercato in un pacchetto. Solo un simbolo di un certo nome può essere in un pacchetto. C'è solo un simbolo CL-USER::FRED.

si scrive:

Quindi, per quanto ne so i simboli uninterned sono i simboli per i quali il valutatore does't creare-simbolo associazione dati internamente.

Questo è sbagliato. I simboli Uninterned sono simboli che non sono internati in alcun pacchetto. Altrimenti stanno perfettamente bene. internato significa registrato nel registro del pacchetto per i relativi simboli.

La s-espressione lettore fa utilizzare il nome del simbolo e il pacchetto per identificare i simboli durante la lettura . Se non esiste un tale simbolo, è internato. Se ce n'è uno, questo viene restituito.

il lettore non guardare in alto i simboli con il loro nome, qui nel pacchetto corrente:

(read-from-string "FOO") -> symbol `FOO` 

una seconda volta:

(read-from-string "FOO") -> symbol `FOO` 

è sempre lo stesso simbolo FOO.

(eq (read-from-string "FOO") (read-from-string "FOO")) -> T 

#:FOO è la sintassi di un simbolo uninterned con il nome FOO. Non è internato in nessun pacchetto. Se il lettore vede questa sintassi, crea un nuovo simbolo non integrato.

(read-from-string "#:FOO") -> new symbol `FOO` 

una seconda volta:

(read-from-string "#:FOO") -> new symbol `FOO` 

Entrambi i simboli sono differenti. Hanno lo stesso nome, ma sono oggetti dati diversi. Non c'è altro registro per i simboli, rispetto ai pacchetti.

(eq (read-from-string "#:FOO") (read-from-string "#:FOO")) -> NIL 

Così nel tuo caso (LET ((#:G4315 1)) (PRINT (INCF #:G4315))), i simboli uninterned sono oggetti diversi. Il secondo è quindi una variabile diversa.

Common Lisp ha un modo per stampare i dati, in modo che l'identità è conservata durante la stampa/lettura:

CL-USER 59 > (macroexpand-1 '(test-macro)) 
(LET ((#:G1996 1)) (PRINT (INCF #:G1996))) 
T 

CL-USER 60 > (setf *print-circle* t) 
T 

CL-USER 61 > (macroexpand-1 '(test-macro)) 
(LET ((#1=#:G1998 1)) (PRINT (INCF #1#))) 
T 

Ora si vede che la s-espressione stampata ha un'etichetta #1= per il primo simbolo . Successivamente, fa riferimento alla stessa variabile. Questo può essere riletto e le identità dei simboli vengono mantenute - anche se il lettore non è in grado di identificare il simbolo osservando il pacchetto.

Così la macro crea un modulo, in cui è generato un solo simbolo. Quando stampiamo quel modulo e vogliamo leggerlo, dobbiamo assicurarci che l'identità dei simboli non integrati sia preservata. La stampa con *print-circle* impostata su T aiuta a farlo.

D: Perché usiamo simboli generati uninterned nelle macro utilizzando GENSYM (generano simbolo)?

In questo modo possiamo avere nuovi simboli unici che non si scontrano con altri simboli nel codice. Hanno un nome con la funzione gensym - di solito con un numero conteggiato alla fine. Dal momento che sono nuovi simboli non inclusi in alcun pacchetto, non possono esserci conflitti di denominazione.

CL-USER 66 > (gensym) 
#:G1999 

CL-USER 67 > (gensym) 
#:G2000 

CL-USER 68 > (gensym "VAR") 
#:VAR2001 

CL-USER 69 > (gensym "PERSON") 
#:PERSON2002 

CL-USER 70 > (gensym) 
#:G2003 

CL-USER 71 > (describe *) 

#:G2003 is a SYMBOL 
NAME   "G2003" 
VALUE   #<unbound value> 
FUNCTION  #<unbound function> 
PLIST   NIL 
PACKAGE  NIL      <------- no package 
+1

Se ho capito bene la tua spiegazione, 'gensym' sarebbe ancora funzionare correttamente anche se non aggiungere un numero contato al nome del simbolo, cioè se ha restituito un simbolo uninterned con lo stesso nome di volta in volta è chiamato (con lo stesso argomento). È corretto? In tal caso: perché aggiunge il numero? In modo che si possa più facilmente dire quali simboli sono uguali e quali non sono nell'output di 'macro-expand'? – sepp2k

+4

@ sepp2k: corretto, il numero serve solo per individuare facilmente dove i simboli non integrati sono diversi e quali potrebbero essere gli stessi. È un aiuto per il debug. Nei dialetti Lisp precedenti (senza pacchetti) avrebbe potuto essere più importante. –

+0

Grazie mille. La spiegazione del * print-circle * è davvero utile per capire come funziona. E grazie per qualche spiegazione sui simboli noninterpretati. – JustAnotherCurious

0

gensym generare un simbolo e quando lo si stampa si ottiene la rappresentazione "stringa" di quel simbolo che non è la stessa cosa di "lettore" rappresentazione cioè la rappresentazione in codice del simbolo.