Common Lisp 中‘ set’,‘ setq’和‘ setf’之间的区别?

Common Lisp 中的“ set”、“ setq”和“ setf”有什么区别?

101671 次浏览

setq就像 set一样,带有引号的第一个参数—— (set 'foo '(bar baz))就像 (setq foo '(bar baz))一样。另一方面,setf确实很微妙——它就像一个“间接”。我建议用 http://www.n-a-n-o.com/lisp/cmucl-tutorials/LISP-tutorial-16.html作为一个更好的方法来开始理解它,这里的任何答案都不能给出... ... 简而言之,尽管 setf将第一个参数作为一个“参考”,因此,例如 (aref myarray 3)将工作(作为 setf的第一个参数)来设置数组中的一个项目。

最初,在 Lisp 中,没有词法变量——只有动态变量 没有 SETQ 或 SETF,只有 SET 功能。

现在写的是:

(setf (symbol-value '*foo*) 42)

是这样写的:

(set (quote *foo*) 42)

最后简称为 SETQ (SET 引用) :

(setq *foo* 42)

然后出现了词法变量,并且 SETQ 也被用来为它们赋值——所以它不再是 SET 的简单包装器。

后来,有人发明了 SETF (SET Field) ,作为给数据结构赋值的一种通用方法,以反映其他语言的 l 值:

x.car := 42;

会被写成

(setf (car x) 42)

为了对称性和通用性,SETF 还提供了 SETQ 的功能。在这一点上,可以正确地说 SETQ 是一个低级原语,而 SETF 是一个高级操作。

然后符号宏出现了。因此,符号宏可以透明地工作,这是意识到,SETQ 将不得不像 SETF 一样,如果“变量”被分配给真的是一个符号宏:

(defvar *hidden* (cons 42 42))
(define-symbol-macro foo (car *hidden*))


foo => 42


(setq foo 13)


foo => 13


*hidden* => (13 . 42)

因此,我们来到了今天: SET 和 SETQ 是老方言的萎缩遗迹,并且很可能会从 Common Lisp 的最终继承者引导出来。

(set ls '(1 2 3 4)) => Error - ls has no value


(set 'ls '(1 2 3 4)) => OK


(setq ls '(1 2 3 4)) => OK - make ls to (quote ls) and then have the usual set


(setf ls '(1 2 3 4)) => OK - same as setq so far BUT


(setf (car ls) 10) => Makes ls '(10 2 3 4) - not duplicated by setq/set

我想在前面的答案中补充一点,setf 是宏,它根据传递的第一个参数调用特定的函数。 将 setf 的宏扩展结果与不同类型的参数进行比较:

(macroexpand '(setf a 1))


(macroexpand '(setf (car (list 3 2 1)) 1))


(macroexpand '(setf (aref #(3 2 1) 0) 1))

对于某些类型的参数,“ setf 函数”将被调用:

(defstruct strct field)
(macroexpand '(setf (strct-field (make-strct)) 1))

您可以使用 setf代替 setsetq,但反之亦然,因为如果变量具有单个元素,那么 setf也可以设置变量的单个元素的值。请看下面的例子:

所有四个示例都将列表(1,2,3)赋给名为 foo 的变量。

(set (quote foo) (list 1 2 3))    ;foo => (1 2 3)
(1 2 3)


(set 'foo '(1 2 3))   ;foo => (1 2 3) same function, simpler expression
(1 2 3)


(setq foo '(1 2 3))   ;foo => (1 2 3) similar function, different syntax
(1 2 3)


(setf foo '(1 2 3))   ;foo => (1 2 3) more capable function
(1 2 3)

setf具有将 foo中列表的一个成员设置为新值的额外功能。

foo                   ;foo => (1 2 3) as defined above
(1 2 3)


(car foo)             ;the first item in foo is 1
1


(setf (car foo) 4)    ;set or setq will fail since (car foo) is not a symbol
4


foo                   ;the fist item in foo was set to 4 by setf
(4 2 3)

但是,您可以定义一个符号宏来表示 foo中的一个项目吗

(define-symbol-macro foo-car (car foo))    ; assumes FOO => (1 2 3)
FOO-CAR


foo-car               ;foo-car is now a symbol for the 1st item in foo
1


(setq foo-car 4)      ;set or setq can set the symbol foo-car
4


foo                   ;Lisp macros are so cool
(4 2 3)

如果您还没有定义变量,并且在以后的代码中不想给它赋值,那么可以使用 defvar

(defvar foo2)
(define-symbol-macro foo-car (car foo2))

我们可以认为 SETSETQ是低级构造。

  • SET可以设置符号的值。

  • SETQ可以设置变量的值。

然后 SETF是一个宏,它提供了多种设置: 符号、变量、数组元素、实例插槽、 ..。

对于符号和变量,可以认为 SETF扩展为 SETSETQ

* (macroexpand '(setf (symbol-value 'a) 10))


(SET 'A 10)




* (macroexpand '(setf a 10))


(SETQ A 10)

因此,SETSETQ被用来实现 SETF的一些功能,SETF是一个更通用的结构。其他一些答案告诉你一个稍微复杂一点的故事,当我们考虑到符号宏。