Common lisp Common Lisp中“set”、“setq”和“setf”之间的区别?

Common lisp Common Lisp中“set”、“setq”和“setf”之间的区别?,common-lisp,Common Lisp,在Common Lisp中,“set”、“setq”和“setf”之间有什么区别?setq就像set一样,第一个参数被引用--(set'foo'(bar baz))就像(setq foo'(bar baz))setf确实很微妙——它就像一个“间接过程”。我建议作为一个更好的方式开始理解它比任何答案在这里可以给出。。。不过,简而言之,setf将第一个参数作为“引用”,因此,例如(aref myarray 3)将(作为setf的第一个参数)在数组中设置项。最初,在Lisp中,没有词汇变量,只有动态变

在Common Lisp中,“set”、“setq”和“setf”之间有什么区别?

setq
就像
set
一样,第一个参数被引用--
(set'foo'(bar baz))
就像
(setq foo'(bar baz))
<另一方面,code>setf确实很微妙——它就像一个“间接过程”。我建议作为一个更好的方式开始理解它比任何答案在这里可以给出。。。不过,简而言之,
setf
将第一个参数作为“引用”,因此,例如
(aref myarray 3)
将(作为
setf
的第一个参数)在数组中设置项。

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

现在写的是:

(setf (symbol-value '*foo*) 42)
(set (quote *foo*) 42)
内容如下:

(setf (symbol-value '*foo*) 42)
(set (quote *foo*) 42)
最终缩写为SETQ(引用SET):

然后词法变量发生了,SETQ也被用来分配给它们——所以它不再是集合的简单包装器

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

我想补充一下以前的答案,即setf是一个宏,它根据作为第一个参数传递的内容调用特定的函数。
(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宏扩展的结果:

(macroexpand '(setf a 1))

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

(macroexpand '(setf (aref #(3 2 1) 0) 1))
对于某些类型的参数,将调用“setf function”:

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

您可以使用
setf
代替
set
setq
,但不能反之亦然,因为
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))

人们可以把
SET
SETQ
看作是低级构造

  • SET
    可以设置符号的值

  • SETQ
    可以设置变量的值

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

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

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

(SET 'A 10)


* (macroexpand '(setf a 10))         

(SETQ A 10)

因此,使用
SET
SETQ
来实现
SETF
的一些功能,这是更一般的构造。当我们考虑符号宏时,其他一些答案会告诉你稍微复杂一点的情况。

Common Lisp总是有词汇变量。你一定是在谈论公共Lisp之前的一些Lisp。如果SET和SETQ是从公共Lisp的后继版本启动的,他们将不得不得到一些替换。它们在高级代码中的使用是有限的,但低级代码(例如,在中实现的代码SETF)需要它们。您选择“car”作为字段而不是可能会混淆为car函数的字段,有什么原因吗?声明
f
实际上代表函数,而不是字段(或表单),并提供引用,因此,虽然字段的setf有一定意义,但看起来可能不正确。摘要:
set
是一个函数。因此它不知道环境<代码>设置无法查看词法变量。它只能设置其参数的符号值
setq
不再是“set quoted”。事实上,
setq
是一种特殊形式,而不是一个宏,这表明。对于setq名称来说最有意义。容易记住。谢谢。我发现你的答案比得票最多的答案更清楚。非常感谢。@Sourav,请不要在示例代码中使用字母“l”(ell)作为变量或符号。很难从视觉上区分数字1。不,我仍然不明白(汽车ls)怎么可能是l值。你知道如何把CLisp翻译成C吗?如何编写CLisp解释器?@user1952009 CLisp是一种常见的Lisp实现。如果您想引用语言本身,CL是最常用的缩写。@user1952009在
(setq ls'((1))
之后,
(setf(car(car(car ls)))5
是未定义的行为,因为
ls
的值是常量(就像在C中修改字符串文字一样)。在
(setq ls(list(list(list 1)))
之后,
(setf(car(car(car ls)))5
的工作原理与C中的
ls->val->val->val=5
类似。这些行为在答案中得到了很好的回答,但公认的答案可能与“setf”中的“f”有一个错误的词源。对的回答是,它用于“函数”,并提供了支持它的参考。