2011-05-02 68 views
1

考虑这段代码:Common Lisp的,参考值和实际值

(defvar lst '(1 1)) 

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

(defun get-y (y lst) 
    (nth y lst)) 

现在让我们假设,我想更改列表称为LST元素的值时, with get-x and the cdr with get-y。 当我试图改变的价值与GET-X(与SETF)一切顺利,但如果我得到-Y它标志着一个错误(缩短)尝试:

;抓到STYLE-WARNING: ;未定义函数:(SETF GET-STUFF)

为什么会发生这种情况?

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

我纠正我的怀疑吗? 如果我是正确的,那么如何才能知道什么仅仅是对一个值和一个实际值的引用?

回答

8

的误差不与宏版本发生,因为,当你认为,表达(setf (get-x some-x some-list) some-value)将扩大(在编译时)成类似(setf (nth some-x some-list) some-value)(不是真的,但the details of setf-expansion是复杂的),并且编译器知道,如何处理该问题(即,存在为功能nth定义的合适的setf扩展器)。

但是,在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)) 

注意,有关于setf -expanders几个约定:

  1. 新的值总是提供作为第一个参数setf功能
  2. 全部setf函数应该返回新的值作为它们的结果(因为这是,整个setf形式应该返回)

在Common Lisp(至少不是C++意义上的)中,没有像“参考”这样的概念,尽管曾经有位于位置的Lisp方言。广义地点表单(即,setf及其机器)与纯C++样式引用的工作方式非常不同。如果您对细节感兴趣,请参阅CLHS。

+0

我实际上并不认为到目前为止在C++意义上的Common Lisp中有引用,这要感谢清除它。我必须弄清楚我的问题的解释,所以这就是我想出的。 用SETF GET-Y这样的名称来定义函数的这件事对我来说是一种全新的概念...无论如何,我会对此做更多的研究,感谢您的帮助! – Johan 2011-05-02 13:05:59

4

SETF是一个宏。

的想法是,设置和读取数据结构元素是两个操作,但通常需要两个不同的名称(或者甚至更复杂的东西)。 SETF现在使您只能使用两个名称:

(get-something x) 

上面显示的是数据结构。反过来简单地说就是:

(setf (get-something x) :foobar) 

上面用X设置数据结构:FOOBAR。

SETF不把(get-something x)当作参考或类似的东西。它只是每个操作都有一个反操作数据库。如果你使用GET-SOMETHING,它知道反操作是什么。

SETF如何知道它?简单:你必须告诉它。

对于NTH操作,SETF知道如何设置第n个元素。这是内置于Common Lisp中的。

对于您自己的GET-Y操作,SETF不具有该信息。你必须告诉它。有关示例,请参阅Common Lisp HyperSpec。一个例子是使用DEFUN和(SETF GET-Y)作为函数名。

还要注意以下问题的风格与你的例子:

  • LST不是一个DEFVAR变量一个好名字。使用* list *作为名称以明确它是由DEFVAR(或类似的)声明的特殊变量。

  • '(1 2)是一个字面常量。如果你编写一个Common Lisp程序,改变它的效果是不确定的。如果您想稍后更改列表,则应该将其与LIST或COPY-LIST之类的内容相提并论。

+0

+1忽略了以文字形式提供的数据的大小。 – Dirk 2011-05-02 12:31:55

+0

是的,我知道我确实应该在全局变量周围使用星号等,我只是非常懒惰,想以尽可能最短的方式写出我想要的东西,我的“真实”代码看起来不像那样。谢谢你的解释! – Johan 2011-05-02 13:04:45