首页 文章

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

提问于
浏览
142

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

6 回答

  • 121

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

    现在写的是:

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

    被写成:

    (set (quote *foo*) 42)
    

    最终abbreviavated到SETQ(SET引用):

    (setq *foo* 42)
    

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

    后来,有人发明了SETF(SET Field)作为向数据结构赋值的通用方法,以镜像其他语言的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的最终继承者中启动 .

  • 4
    (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
    
  • 15

    setq 就像 set ,引用第一个arg - (set 'foo '(bar baz)) 就像 (setq foo '(bar baz)) . 另一方面, setf 确实很微妙 - 它就像一个"indirection" . 我建议http://www.n-a-n-o.com/lisp/cmucl-tutorials/LISP-tutorial-16.html作为开始理解它的一种更好的方式,而不是这里的任何答案都可以...简而言之, setf 将第一个参数作为"reference",以便例如 (aref myarray 3) 将在数组中设置项目(作为 setf 的第一个arg) .

  • 149

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

    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))
    
  • 9

    人们可以认为 SETSETQ 是低级构造 .

    • SET 可以设置符号的值 .

    • SETQ 可以设置变量的值 .

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

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

    * (macroexpand '(setf (symbol-value 'a) 10))
    
    (SET 'A 10)
    
    
    * (macroexpand '(setf a 10))         
    
    (SETQ A 10)
    

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

  • 20

    我想在之前的答案中添加setf是宏,它根据作为第一个参数传递的内容调用特定函数 . 将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))
    

相关问题