符号可以在包中也可以不被实习。可以查找并找到包含在包中的符号。无法在包中查找 uninterned 符号。一个包中只能有一个特定名称的符号。只有一个符号CL-USER::FRED
。
你写:
据我所知,uninterned 符号是评估器不会在内部创建符号数据绑定的符号。
那是错误的。Uninterned Symbols 是没有在任何包中的符号。否则他们完全没问题。interned表示在包的注册表中为其符号注册。
s-expression阅读器在阅读过程中确实使用符号名称和包来识别符号。如果没有这样的符号,它就会被拘留。如果有一个,则返回这个。
读者确实在当前包中按名称查找符号:
(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*
使用set to打印T
有助于做到这一点。
问:为什么我们在宏中通过 using GENSYM
( generate symbol ) 使用 uninterned 生成的符号?
这样我们就可以拥有独特的新符号,它们不会与代码中的其他符号发生冲突。他们通过函数获得名称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