何时在Emacs Lisp中引用符号
我开始学习使用Emacs Lisp编程。我被符号引用弄糊涂了。 例如:何时在Emacs Lisp中引用符号,emacs,lisp,elisp,Emacs,Lisp,Elisp,我开始学习使用Emacs Lisp编程。我被符号引用弄糊涂了。 例如: (progn (setq a '(1 2)) (prin1 a) (add-to-list 'a 3) (prin1 a) (setcar a 4) (prin1 a) (push 5 a) "" ) 为什么“添加到列表”函数需要一个带引号的符号作为它的第一个参数,而“setcar”和“push”函数不需要参数引号?函数在执行之前评估它们的参数,所以在需要传递实际符号(例如,作为指向某个数据
(progn
(setq a '(1 2))
(prin1 a)
(add-to-list 'a 3)
(prin1 a)
(setcar a 4)
(prin1 a)
(push 5 a)
""
)
为什么“添加到列表”函数需要一个带引号的符号作为它的第一个参数,而“setcar”和“push”函数不需要参数引号?函数在执行之前评估它们的参数,所以在需要传递实际符号(例如,作为指向某个数据结构的指针)时引用,而在它是变量值时不引用
添加到列表
对其第一个参数执行就地变异,因此它需要一个带引号的符号
push
不是一个函数,而是一个宏;这就是为什么它能够接受不带引号的参数而不进行求值。内置表单,如setcar
,也没有这个限制。这里有一个图表,表示符号a
及其后的值(setq a'(12))
。框是基本数据结构(符号和conse),箭头是指针(其中一段数据引用另一段数据)。(我正在简化一点。)
表达式”(12)
构建右侧的两个conse,这两个conse构成了一个两元素列表。表达式(setq a'(1 2))
创建符号a
,如果它不存在,则使其“变量槽”(包含符号值的部分)指向新创建的列表setq
是一个内置宏,(setqa'(12))
是(set'a'(12))
的缩写。set
的第一个参数是要修改的符号,第二个参数是要将符号的变量槽设置为的值
(添加到列表'a3)
相当于此处的(设置'a(cons3a))
,因为列表中没有3。此表达式有四个功能:
3
a
(即复制a
的变量槽的内容)的前一个(和当前)值a
的变量槽设置为新的cons单元格 symbol cons cons cons
+-------+----------+ +------+--|---+ +------+------+ +------+------+
|name: |variable: | |car: |cdr: | |car: |cdr: | |car: |cdr: |
| a | | | | 3 | | | | 1 | | | | 2 | nil |
+-------+----|-----+ +------+--|---+ +------+--|---+ +------+------+
| ↑ | ↑ | ↑
+-------------+ +-------+ +-------+
symbol cons cons cons
+-------+----------+ +------+--|---+ +------+------+ +------+------+
|name: |variable: | |car: |cdr: | |car: |cdr: | |car: |cdr: |
| a | | | | 4 | | | | 1 | | | | 2 | nil |
+-------+----|-----+ +------+--|---+ +------+--|---+ +------+------+
| ↑ | ↑ | ↑
+-------------+ +-------+ +-------+
调用setcar
不会创建任何新的数据结构,也不会作用于符号a
,而是作用于其值,即car
当前包含3的cons单元格。在(setcar a 4)
之后,数据结构如下所示:
symbol cons cons cons
+-------+----------+ +------+--|---+ +------+------+ +------+------+
|name: |variable: | |car: |cdr: | |car: |cdr: | |car: |cdr: |
| a | | | | 3 | | | | 1 | | | | 2 | nil |
+-------+----|-----+ +------+--|---+ +------+--|---+ +------+------+
| ↑ | ↑ | ↑
+-------------+ +-------+ +-------+
symbol cons cons cons
+-------+----------+ +------+--|---+ +------+------+ +------+------+
|name: |variable: | |car: |cdr: | |car: |cdr: | |car: |cdr: |
| a | | | | 4 | | | | 1 | | | | 2 | nil |
+-------+----|-----+ +------+--|---+ +------+--|---+ +------+------+
| ↑ | ↑ | ↑
+-------------+ +-------+ +-------+
push
是一个宏;这里,(按5 a)
相当于(设置'a(cons 5 a))
setq
和push
是宏(setq
是一种“特殊形式”,在这里,就我们而言,它指的是一种宏,其定义内置于解释器中,而不是在Lisp中提供)。宏接收未计算的参数,可以选择是否展开它们set
、setcar
和add to list
都是函数,它们会接受对其参数的求值。对符号求值返回其变量槽的内容,例如,在初始(setq a'(1 2))
之后,符号a
的值是其car包含1
的cons单元格
如果您仍然感到困惑,我建议您尝试一下
(setq b a)
,看看当您对a
(作用于符号a
)进行操作时,哪些表达式会修改b
(作用于符号a
)而哪些表达式不会修改.到目前为止给出的其他答案澄清了quote
的使用以及函数与宏和特殊形式之间的区别
然而,他们没有进入问题的另一部分:为什么添加到列表中?为什么它要求它的第一个参数是符号?这与它是否评估论点是一个独立的问题。这是添加到列表
设计背后的真正问题
可以想象,addtolist
计算了它的参数,并期望第一个参数的值是一个列表,然后将第二个参数的值作为元素添加到该列表中并返回结果(新列表或相同列表)。这将允许您执行(添加到列表foo'shoe)
将符号shoe
添加到列表中,该符号是foo
的值,例如(1 2扣)
,以给出(1 2扣鞋)
关键是这样一个函数不会很有用。为什么?因为列表值不一定是可访问的。变量foo
可能被认为是访问它的一种方式——指向它的“句柄”或“指针”。但对于函数返回的列表,情况并非如此。返回的列表可以由新的列表结构组成,并且通常没有任何东西(没有变量)指向该列表。函数addtolist
从未看到符号(变量)foo
——它无法知道它作为第一个参数接收的列表值绑定到foo
。如果addtolist
就是这样设计的,那么您仍然需要将其返回的结果分配给list变量
现在,addtolist
评估其参数,因为它是一个函数,但这并不能解释太多。它需要一个符号作为其第一个参数的值。它期望该变量(符号)的值是一个列表。它将其第二个参数的值添加到列表中(可能会更改列表结构),并设置变量的值,即其第一个参数在该列表中的值
底线:它需要一个符号作为arg,因为它的任务是为该符号指定一个新值(新值是相同的列表值或与前面添加的新列表元素相同的值)
是的,另一条路是我们