Символы без символов

Есть что-то, чего я не могу понять в Common lisp.

Предположим, я пишу макрос, похожий на этот:

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

Чем я могу сделать

> (test-macro)
2
2

Теперь я хочу посмотреть, как это расширяется

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

Хорошо. Есть уникальные символы, сгенерированные с помощью gensym, которые были напечатаны как непереданные

Так что, насколько я знаю, неинтернизированные символы являются символами, для которых оценщик не создает привязку данных к символам внутри.

Таким образом, если мы развернем макрос до этой формы, должна появиться ошибка (incF#:G4315). Чтобы проверить это, мы можем просто оценить эту форму в REPL:

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

Так почему же макрос, который расширяется до этой строки, работает отлично, а сама форма - нет?

2 ответа

Решение

Символы могут быть встроены в пакет или нет. Символ, помещенный в пакет, можно найти и найти. Невозможный символ не может быть найден в пакете. В упаковке может быть только один символ с определенным именем. Есть только один символ CL-USER::FRED,

Вы пишете:

Так что, насколько я знаю, неинтернизированные символы являются символами, для которых оценщик не создает привязку данных к символам внутри.

Это неверно. Uninterned символы являются символами, которые не интернированы ни в одной упаковке. В остальном они в порядке. interned означает зарегистрированный в реестре пакета для его символов.

Считыватель s-выражений использует имя символа и пакет для идентификации символов во время чтения. Если такого символа нет, он интернируется. Если есть один, то этот возвращается.

Читатель ищет символы по их названию, в текущем пакете:

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

второй раз:

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

это всегда один и тот же символ FOO,

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

#:FOO это синтаксис для неустранимого символа с именем FOO, Он не интернирован ни в одной упаковке. Если читатель видит этот синтаксис, он создает новый непостоянный символ.

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

второй раз:

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

Оба символа разные. У них одинаковое имя, но они разные объекты данных. Нет другого реестра для символов, кроме пакетов.

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

Таким образом, в вашем случае (LET ((#:G4315 1)) (PRINT (INCF #:G4315))), непереданные символы - это разные объекты. Второй - это другая переменная.

Common Lisp имеет способ печати данных, так что идентичность сохраняется во время печати / чтения:

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

Теперь вы видите, что напечатанное s-выражение имеет метку #1= для первого символа. Затем он ссылается на ту же переменную. Это может быть прочитано обратно, и идентичность символов сохранена - даже если читатель не может идентифицировать символ, посмотрев на пакет.

Таким образом, макрос создает форму, в которой генерируется только один символ. Когда мы распечатываем эту форму и хотим прочитать ее обратно, мы должны убедиться, что идентичность не связанных между собой символов сохраняется. Печать с *print-circle* установлен в T помогает сделать это.

В: Почему мы используем не сгенерированные символы в макросах, используя GENSYM (создать символ)?

Таким образом, мы можем иметь уникальные новые символы, которые не конфликтуют с другими символами в коде. Они получают имя по функции gensym - обычно с подсчитанным числом в конце. Так как они являются новыми новыми символами, не содержащимися ни в одном пакете, не может быть никакого конфликта имен.

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

gensym генерировать символ, и когда вы его печатаете, вы получаете "строковое" представление этого символа, которое отличается от представления "читателя", то есть кодового представления символа.

Другие вопросы по тегам