关于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生成的唯一符号打印为uninterned。
据我所知,未分隔符号是评估者不会在内部创建符号数据绑定的符号。
因此,如果我们宏扩展到那个形式,那么应该有一个错误(incf#:G4315)。 为了测试这个,我们可以在REPL中评估该表单:
> (LET ((#:G4315 1)) (PRINT (INCF #:G4315)))
*** - SETQ: variable #:G4315 has no value
那么为什么扩展到这个字符串的宏可以正常工作而表单本身不起作用呢?
答案 0 :(得分:16)
符号可以在包中实现。可以查找并找到包中的符号。无法在包中查找未处理的符号。包中只能包含某个名称的一个符号。只有一个符号CL-USER::FRED
。
你写:
据我所知,未分隔符号是评估者不会在内部创建符号数据绑定的符号。
那是错的。未分隔符号是在任何包中不 interned 的符号。否则他们完全没问题。 interned 表示注册在包的注册表中的符号。
s-expression 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
(生成符号)在宏中使用未处理的生成符号?
这样我们就可以拥有独特的新符号,这些符号不会与代码中的其他符号冲突。它们通过函数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
答案 1 :(得分:0)
gensym
生成一个符号,当你打印它时,你会得到该符号的“字符串”表示,这与“读者”表示不同,即符号的代码表示。