Common Lisp,对值和实际值的引用

Joh*_*han 1 macros reference common-lisp

考虑一下这段代码:

(defvar lst '(1 1))

(defmacro get-x (x lst)
  `(nth ,x ,lst))

(defun get-y (y lst)
  (nth y lst))
Run Code Online (Sandbox Code Playgroud)

现在让我们假设我想要更改名为lst的列表元素的值,使用get-xcar和使用get-ycdr.当我尝试用get-x(使用setf)更改值时,一切都很顺利但是如果我用get-y尝试它发出错误信号(缩短):

; 陷入STYLE-WARNING:; 未定义的功能:(SETF GET-STUFF)

为什么会这样?

我自己也怀疑,这是因为宏只是展开和函数第n简单地返回到列表中,并在另一方面,功能元素的值的参考评估函数调用到第n和返回参考价值的价值(听起来令人困惑).

我的怀疑是否正确?如果我是正确的,那么如何知道什么只是对价值和实际价值的引用?

Dir*_*irk 8

宏版本不会发生错误,因为正如您所假设的那样,表达式(setf (get-x some-x some-list) some-value)将(在编译时)扩展为类似的东西(setf (nth some-x some-list) some-value)(不是真的,但是-expansion 的细节setf很复杂),并且编译器知道,如何处理它(即,有一个合适的setf扩展器为功能定义nth).

但是,在这种情况下get-y,编译器没有setf扩展器,除非你提供一个.最简单的方法是

(defun (setf get-y) (new-value x ls)    ; Note the function's name: setf get-y 
    (setf (nth x ls) new-value))
Run Code Online (Sandbox Code Playgroud)

注意,有关setf-expanders 的一些约定:

  1. 始终将新值作为setf函数的第一个参数提供
  2. 所有setf函数都应该返回新值作为结果(因为这是整个setf表单应该返回的内容)

顺便说一下,在Common Lisp中没有像"参考"那样的概念(至少在C++意义上没有),尽管曾经有Lisp方言有定位.广义位置表单(即,setf及其机制)与普通C++样式引用的工作方式完全不同.如果您对细节感到好奇,请参阅CLHS.