何时在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”函数不需要参数引号?函数在执行之前评估它们的参数,所以在需要传递实际符号(例如,作为指向某个数据

我开始学习使用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”函数不需要参数引号?

函数在执行之前评估它们的参数,所以在需要传递实际符号(例如,作为指向某个数据结构的指针)时引用,而在它是变量值时不引用

添加到列表
对其第一个参数执行就地变异,因此它需要一个带引号的符号


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。此表达式有四个功能:

  • 创建一个新的cons单元
  • 将新cons单元的car字段设置为
    3
  • 将新cons单元格的cdr字段设置为
    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,因为它的任务是为该符号指定一个新值(新值是相同的列表值或与前面添加的新列表元素相同的值)

    是的,另一条路是我们