188

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

4

6 回答 6

187

最初,在 Lisp 中,没有词法变量——只有动态变量。而且没有 SETQ 或 SETF,只有 SET 函数。

现在写成:

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

写成:

(set (quote *foo*) 42)

最终缩写为 SETQ (SET Quoted):

(setq *foo* 42)

然后出现了词法变量,并且 SETQ 也开始用于对它们进行赋值——因此它不再是 SET 的简单包装器。

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

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 的最终继承者中被淘汰。

于 2009-05-15T16:36:39.323 回答
169
(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
于 2009-09-25T06:57:34.640 回答
22

setq就像set引用的第一个 arg 一样 -(set 'foo '(bar baz))就像(setq foo '(bar baz)). setf,另一方面,确实很微妙——它就像一个“间接”。我建议http://www.nano.com/lisp/cmucl-tutorials/LISP-tutorial-16.html作为开始理解它的更好方法,比这里的任何答案都可以给出......总之,不过,setf需要第一个参数作为“参考”,因此 eg(aref myarray 3)将起作用(作为第一个参数setf)来设置数组内的项目。

于 2009-05-15T16:18:14.623 回答
22

您可以使用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))
于 2012-08-01T23:32:57.147 回答
17

可以认为是SET低级SETQ构造。

  • SET可以设置符号的值。

  • SETQ可以设置变量的值。

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

对于符号和变量,可以认为好像SETF展开为SETand SETQ

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

(SET 'A 10)


* (macroexpand '(setf a 10))         

(SETQ A 10)

所以SETSETQ用于实现 的一些功能SETF,这是更通用的构造。当我们考虑符号宏时,其他一些答案会告诉您稍微复杂的故事。

于 2013-08-08T09:25:55.917 回答
5

我想在之前的答案中添加 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))
于 2012-07-16T13:01:47.480 回答