【问题标题】:Difference between `set`, `setq`, and `setf` in Common Lisp?Common Lisp中的`set`,`setq`和`setf`之间的区别?
【发布时间】:2010-10-26 13:16:46
【问题描述】:

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

【问题讨论】:

  • 这些行为在答案中得到了很好的回答,但接受的答案可能对“setf”中的“f”有错误的词源。 What does the f in setf stand for? 的答案说它是为了“功能”,并提供了支持它的参考。

标签: common-lisp


【解决方案1】:

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

现在写成:

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

写成:

(set (quote *foo*) 42)

最终缩写为 SETQ (SET Quoted):

(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 的最终继承者中被淘汰。

【讨论】:

  • Common Lisp 总是有词法变量。你一定是在 Common Lisp 之前谈论一些 Lisp。
  • 如果 SET 和 SETQ 要从 Common Lisp 继任者引导,他们将不得不得到一些替代品。它们在高级代码中的使用是有限的,但低级代码(例如,实现 SETF 的代码)需要它们。
  • 您选择“汽车”作为字段而不是可能会混淆汽车功能的字段是否有原因?
  • This answer to What does the f in setf stand for? 声称 f 实际上代表 function,而不是 field(或 form b>,就此而言),并提供参考,因此虽然 setf for field 有一定的意义,但它看起来可能不正确。
  • 总结:set 是一个函数。因此它不知道环境。 set 看不到词法变量。它只能设置其参数的符号值。 setq 不再“设置引用”。 setq 是一种特殊形式,而不是宏,这一事实表明了这一点。
【解决方案2】:
(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

【讨论】:

  • 我发现你的答案比投票最高的答案更清楚。非常感谢。
  • @Sourav,请不要在示例代码中使用字母“l”(ell)作为变量或符号。很难从视觉上区分数字 1。
  • 不,我仍然不明白 (car ls) 如何成为左值。你知道如何将 CLisp 翻译成 C 吗?以及如何编写CLisp解释器?
  • @user1952009 clisp 是一种 Common 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 一样工作。
【解决方案3】:

可以将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 代替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))
    

    【讨论】:

      【解决方案5】:

      我想在之前的答案中添加 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))
      

      【讨论】:

        【解决方案6】:

        setq 就像 set 带有引用的第一个参数 -- (set 'foo '(bar baz)) 就像 (setq foo '(bar baz))。另一方面,setf 确实很微妙——它就像一个“间接”。我建议http://www.n-a-n-o.com/lisp/cmucl-tutorials/LISP-tutorial-16.html 作为开始理解它的更好方法,比这里的任何答案都可以给出......简而言之,setf 将第一个参数作为“参考”,例如(aref myarray 3) 将起作用(作为setf 的第一个参数)在数组中设置一个项目。

        【讨论】:

        • 对 setq 名称最有意义。容易记住。谢谢。
        猜你喜欢
        • 2013-08-12
        • 1970-01-01
        • 2010-11-01
        • 2012-02-14
        • 1970-01-01
        • 2017-05-09
        • 2012-01-16
        • 1970-01-01
        • 2012-06-28
        相关资源
        最近更新 更多