符号可以被嵌入到包中,也可以不被嵌入。 被嵌入到包中的符号可以被查找和找到。无法在包中查找 uninterned 符号。一个包中只能有一个特定名称的符号。只有一个符号CL-USER::FRED。
你写:
据我所知,uninterned 符号是评估器不会在内部为其创建符号数据绑定的符号。
错了。 Uninterned 符号是在任何包中都没有interned 的符号。否则他们完全没问题。 interned 表示 已在包的 registry 中为其符号注册。
s 表达式reader 在reader 期间确实使用符号名称和包来标识符号。如果没有这样的符号,它就会被拘留。如果有,则返回这个。
读者确实会在当前包中按名称查找符号:
(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(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