普通Lisp中的set,setq和setf之间的区别?


166

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


9
这些问题的行为在答案中得到了很好的回答,但是被接受的答案在“ setf”中对于“ f”可能具有错误的词源。setf中的f代表什么的答案说它是针对“功能”的,并提供了备份的参考。
约书亚·泰勒

Answers:


169

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

现在写成:

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

被写为:

(set (quote *foo*) 42)

最终缩写为SETQ(SET引用):

(setq *foo* 42)

然后发生了词法变量,并且SETQ也被用于为其赋值-因此它不再是SET的简单包装。

后来,有人发明了SETF(SET字段)作为将值分配给数据结构的通用方法,以反映其他语言的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的最终继承者那里得到引导。


45
Common Lisp始终具有词法变量。在Common Lisp之前,您必须先谈论一些Lisp。
Rainer Joswig 2009年

4
如果要从Common Lisp后继程序启动SET和SETQ,则它们将必须进行替换。它们在高级代码中的使用受到限制,但是低级代码(例如,实现SETF的代码)需要它们。
斯万特,2009年

13
您是否选择“汽车”作为字段,而不是因为汽车功能而感到困惑?
drudru

9
这个setf中的f代表什么的答案声称f实际上代表的是function,而不是field(或form),并提供了引用,因此,虽然field的setf有一定意义,但看起来可能不正确。
约书亚·泰勒

1
摘要:set是一个功能。因此,它不知道环境。set无法看到词法变量。它只能设置其参数的符号值。setq不再“设置引号”。这setq是一种特殊的形式,而不是宏,这一事实表明了这一点。
KIM Taegyoon,

142
(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

12
我发现您的答案比票数最高的答案更清楚。非常感谢。
CDR

2
@Sourav,请不要在示例代码中将字母“ l”(ell)用作变量或符号。很难从视觉上与数字1区分开
。– DavidBooth 2014年

不,我仍然不知道(car ls)如何成为l值。您了解如何将CLisp转换为C吗?以及如何编写CLisp解释器?
2015年

@ user1952009 clisp是Common Lisp的一种实现。如果要引用语言本身,CL是最常用的缩写。
ssice

2
@ user1952009之后(setq ls '(((1))))(setf (car (car (car ls))) 5)是未定义的行为,因为的值ls是常量(例如在C中修改字符串文字)。之后(setq ls (list (list (list 1))))(setf (car (car (car ls))) 5)就像ls->val->val->val = 5在C中一样工作
。– kyle

21

setq就像set带引号的第一个arg (set 'foo '(bar baz))一样(setq foo '(bar baz))setf另一方面,确实是微妙的-就像“间接”。我建议http://www.nano.com/lisp/cmucl-tutorials/LISP-tutorial-16.html作为一种更好的方式开始理解它比任何答案在这里可以给......总之,虽然,setf取将第一个参数用作“引用”,以便例如(aref myarray 3)将(作为的第一个arg setf)用于在数组内设置项目。


1
最有意义的是setq名称。容易记住。谢谢。
CDR

17

您可以使用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))

13

一个能想到的SETSETQ为低级别的构造。

  • SET 可以设置符号的值。

  • SETQ 可以设置变量的值。

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

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

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

(SET 'A 10)


* (macroexpand '(setf a 10))         

(SETQ A 10)

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


4

我想在以前的答案中加上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))
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.