Common lisp Common Lisp中“set”、“setq”和“setf”之间的区别?
在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中,没有词汇变量,只有动态变
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”有一个错误的词源。对的回答是,它用于“函数”,并提供了支持它的参考。