不加工的符号符号

Jus*_*ous 10 lisp symbols common-lisp gensym

关于Common lisp,我无法理解.

假设我正在编写一个类似于此的宏:

(defmacro test-macro () 
   (let ((result (gensym))) 
      `(let ((,result 1))
         (print (incf ,result))))) 
Run Code Online (Sandbox Code Playgroud)

比我能做的

> (test-macro)
2
2
Run Code Online (Sandbox Code Playgroud)

现在我想看看它是如何扩展的

> (macroexpand-1 '(test-macro))
(LET ((#:G4315 1)) (PRINT (INCF #:G4315))) ;
T
Run Code Online (Sandbox Code Playgroud)

好.使用gensym生成的唯一符号打印为uninterned.

因此,据我所知,未分隔符号是评估者不在内部创建符号数据绑定的符号.

所以,如果我们宏扩展到那个形式,那么应该有一个错误(incf#:G4315).为了测试这个,我们可以在REPL中评估该表单:

> (LET ((#:G4315 1)) (PRINT (INCF #:G4315)))
*** - SETQ: variable #:G4315 has no value
Run Code Online (Sandbox Code Playgroud)

那么为什么扩展到这个字符串的宏工作得很好而表单本身没有呢?

Rai*_*wig 16

符号可以包含在包中或不包含在内.可以查找并找到包裹中的符号.无法在包中查找未处理的符号.包中只能包含某个名称的一个符号.只有一个符号CL-USER::FRED.

你写道:

因此,据我所知,未分隔符号是评估者不在内部创建符号数据绑定的符号.

那是错的.Uninterned符号是未符号扣留在任何包.否则他们完全没问题.实习方式登记在包的注册表它的符号.

s表达式读取器确实使用符号名称和包来在读取期间识别符号.如果没有这样的符号,那就是实习.如果有,则返回此项.

读者会根据名称查找符号,在当前包中:

 (read-from-string "FOO") -> symbol `FOO`
Run Code Online (Sandbox Code Playgroud)

第二次:

 (read-from-string "FOO") -> symbol `FOO`
Run Code Online (Sandbox Code Playgroud)

它始终是相同的符号FOO.

 (eq (read-from-string "FOO") (read-from-string "FOO"))  -> T
Run Code Online (Sandbox Code Playgroud)

#:FOO是具有名称的未分隔符号的语法FOO.它不是任何包裹中的实习.如果读者看到此语法,则会创建一个新的未处理符号.

 (read-from-string "#:FOO") -> new symbol `FOO`
Run Code Online (Sandbox Code Playgroud)

第二次:

 (read-from-string "#:FOO") -> new symbol `FOO`
Run Code Online (Sandbox Code Playgroud)

两个符号都不同.它们具有相同的名称,但它们是不同的数据对象.除了包之外,没有其他符号注册表.

 (eq (read-from-string "#:FOO") (read-from-string "#:FOO"))  -> NIL
Run Code Online (Sandbox Code Playgroud)

因此,在您的情况下(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
Run Code Online (Sandbox Code Playgroud)

现在您看到打印的s表达式具有#1=第一个符号的标签.然后它引用相同的变量.这可以回读并保留符号标识 - 即使读者无法通过查看包来识别符号.

因此,宏创建一个表单,其中只生成一个符号.当我们打印该表单并想要将其读回时,我们需要确保保留未加密符号的标识.使用*print-circle*set进行打印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
Run Code Online (Sandbox Code Playgroud)

  • @ sepp2k:正确,这个数字只是为了让人们很容易发现不间断符号不同的地方,哪些可能是相同的.这是一个调试帮助.在早期的Lisp方言(没有包)中,它可能更重要. (4认同)