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

Ric*_*ins 158 common-lisp

Common Lisp中"set","setq"和"setf"有什么区别?

Sta*_*mer 165

最初,在Lisp中,没有词汇变量 - 只有动态变量.并且没有SETQ或SETF,只有SET功能.

现在写的是:

(setf (symbol-value '*foo*) 42)
Run Code Online (Sandbox Code Playgroud)

被写成:

(set (quote *foo*) 42)
Run Code Online (Sandbox Code Playgroud)

最终abbreviavated到SETQ(SET引用):

(setq *foo* 42)
Run Code Online (Sandbox Code Playgroud)

然后发生了词汇变量,并且SETQ也被用于赋值给它们 - 所以它不再是SET的简单包装器.

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

x.car := 42;
Run Code Online (Sandbox Code Playgroud)

将写成

(setf (car x) 42)
Run Code Online (Sandbox Code Playgroud)

为了对称性和通用性,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)
Run Code Online (Sandbox Code Playgroud)

所以我们到达现在:SET和SETQ是老式方言的萎缩遗骸,并且可能会从Common Lisp的最终继承者中启动.

  • Common Lisp总是有词法变量.你必须在Common Lisp之前谈论一些Lisp. (43认同)
  • 有没有理由你选择'汽车'作为一个领域,而不是可能会被汽车功能混淆的东西? (12认同)
  • [这个答案](http://stackoverflow.com/a/23808420/1281433)以[setf中的f代表什么?](http://stackoverflow.com/q/23808189/1281433)声称` f`实际上代表**函数**,而不是**字段**(或**形式**,就此而言),并提供引用,所以虽然字段的setf有一定意义,但看起来它可能不会是对的. (8认同)
  • 如果要从Common Lisp后继者启动SET和SETQ,他们将不得不进行一些替换.它们在高级代码中的使用是有限的,但是低级代码(例如,实现代码SETF)需要它们. (4认同)

小智 132

(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
Run Code Online (Sandbox Code Playgroud)

  • 我发现你的答案要比最高票的答案更明确.非常感谢. (12认同)
  • @Sourav,请不要在示例代码中使用字母"l"(ell)作为变量或符号.在视觉上与数字1区分太难了. (2认同)
  • @ user1952009在((setq ls'((((1))))`之后,((setf(car(car(car ls)))5)是不确定的行为,因为`ls`的值是常量(例如修改C中的字符串文字)。在`{setq ls(list(list(list(list 1)))))`之后,`{setf(car(car(car(car ls))))5)`的作用类似于`ls-> val-> val-> val = 5在C中。 (2认同)

Ale*_*lli 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)setf在数组中设置项目(作为第一个arg ).


dan*_*lmo 16

您可以使用setf替代setsetq反之则不行,因为setf还可以设置一个变量的各个元素的值,如果变量具有单个元素.见下面的exaples:

所有四个示例都将列表(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)
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

但是,您可以定义一个代表其中单个项目的符号宏 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)
Run Code Online (Sandbox Code Playgroud)

defvar如果您尚未定义变量并且不希望在代码中稍后为其赋值,则可以使用.

(defvar foo2)
(define-symbol-macro foo-car (car foo2))
Run Code Online (Sandbox Code Playgroud)


Rai*_*wig 12

人们可以想到SETSETQ成为低级别的结构.

  • SET 可以设置符号的值.

  • SETQ 可以设置变量的值.

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

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

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

(SET 'A 10)


* (macroexpand '(setf a 10))         

(SETQ A 10)
Run Code Online (Sandbox Code Playgroud)

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


Fil*_*ipp 5

我想添加到先前的答案中, setf 是宏,它根据作为第一个参数传递的内容调用特定函数。比较 setf 与不同类型参数的宏展开结果:

(macroexpand '(setf a 1))

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

(macroexpand '(setf (aref #(3 2 1) 0) 1))
Run Code Online (Sandbox Code Playgroud)

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

(defstruct strct field)
(macroexpand '(setf (strct-field (make-strct)) 1))
Run Code Online (Sandbox Code Playgroud)