我现在已经了解了Lisp中的数组和aref。到目前为止,它很容易理解,而且它的工作原理就像一种特性:
(defparameter *foo* (make-array 5))
(aref *foo* 0) ; => nil
(setf (aref *foo* 0) 23)
(aref *foo* 0) ; => 23让我困惑的是当你把aref和setf结合在一起的时候发生的“魔法”。似乎aref知道它的调用上下文,然后决定是返回一个值,还是返回一个setf可以使用的位置。
无论如何,目前我只是认为这是理所当然的,不要过多地考虑内部的工作方式。
但是现在我想创建一个函数,将*foo*数组的一个元素设置为一个预定义的值,但我不想硬编码*foo*数组,而是想交出一个位置:
(defun set-23 (place)
…)基本上,这个函数将place设为23,不管place是什么。我最初的天真做法是
(defun set-23 (place)
(setf place 23))并使用以下方法进行调用:
(set-23 (aref *foo* 0))这不会导致错误,但也不会改变*foo*。我猜对aref的调用将解析为nil (因为数组当前是空的),所以这意味着
(setf nil 23)正在运行,但当我在REPL中手动尝试此操作时,会收到一个错误,告诉我:
0是一个常数,不能用作变量。
(这绝对是有道理的!)
最后,我有两个问题:
set-23函数正常工作?我还想为此使用一个thunk来推迟aref的执行,如下所示:
(defun set-23 (fn)
(setf (funcall fn) 23))但是,正如Lisp现在告诉我的那样,当我试图定义这个函数时,这已经出现了一个错误:
(SETF FUNCALL)只为#‘符号的函数定义。
再说一次,我不知道这是为什么。为什么结合使用setf和funcall显然可以用于指定的函数,而不适用于lambdas,例如?
PS:在“Lisp之地”(我目前正在阅读它来了解Lisp)中写着:
实际上,
setf中的第一个参数是Common的一种特殊的子语言,称为广义引用。并不是每个Lisp命令都允许在通用引用中使用,但是仍然可以放入一些非常复杂的内容:…
好吧,我想这就是这里的原因(或者至少是其中一个原因),为什么所有这些都不像我期望的那样起作用,但我还是很想了解更多:-)
发布于 2014-06-07 08:10:42
一个地方不是物理的,它只是一个概念,任何我们可以得到/设置一个值的东西。所以一个地方一般是不能归还或通过的。Lisp开发人员想要一种简单的方法,从知道getter是什么开始就可以很容易地猜出一个setter。因此,我们用一个周围的setf表单编写了getter,Lisp指出了如何设置如下内容:
(slot-value vehicle 'speed) ; gets the speed
(setf (slot-value vehicle 'speed) 100) ; sets the speed如果没有SETF,我们需要一个具有其名称的setter函数:
(set-slot-value vehicle 'speed 100) ; sets the speed为了设置数组,我们需要另一个函数名:
(set-aref 3d-board 100 100 100 'foo) ; sets the board at 100/100/100请注意,上述setter函数可能在内部存在。但是你不需要用setf来了解他们。
结果:我们最终得到了许多不同的setter函数名。SETF机制用一种通用语法替换了所有这些机制。你知道那个电话吗?那你也认识那个策划人。它只是围绕getter调用加上新值的setf。
另一个例子
world-time ; may return the world time
(setf world-time (get-current-time)) ; sets the world time等等..。
还请注意,只有宏处理设置位置:setf、push、pushnew、remf、.只有那些人,你才能建立一个地方。
(defun set-23 (place)
(setf place 23))上面可以写,但是place只是一个变量名。你不能通过一个地方。让我们重命名它,它不会改变任何事物,但会减少混淆:
(defun set-23 (foo)
(setf foo 23))这里,foo是一个局部变量。局部变量是一个位置。我们可以设置的东西。因此我们可以使用setf来设置变量的局部值。我们不设置被传入的东西,而是设置变量本身。
(defmethod set-24 ((vehicle audi-vehicle))
(setf (vehicle-speed vehicle) 100))在上面的方法中,vehicle是一个变量,它绑定到类audi-vehicle的一个对象。为了设置它的速度,我们使用setf调用编写器方法。
李斯特是从哪里认识作家的?例如,类声明生成一个:
(defclass audi-vehicle ()
((speed :accessor vehicle-speed))):accessor vehicle-speed声明导致生成读取和设置函数。
setf宏查看注册设置程序的宏展开时间。就这样。所有setf操作看起来都很相似,但是下面的Lisp知道如何设置。
下面是一些SETF使用的示例,扩展如下:
将数组项设置为索引:
CL-USER 86 > (pprint (macroexpand-1 '(setf (aref a1 10) 'foo)))
(LET* ((#:G10336875 A1) (#:G10336876 10) (#:|Store-Var-10336874| 'FOO))
(SETF::\"COMMON-LISP\"\ \"AREF\" #:|Store-Var-10336874|
#:G10336875
#:G10336876))设置变量:
CL-USER 87 > (pprint (macroexpand-1 '(setf a 'foo)))
(LET* ((#:|Store-Var-10336877| 'FOO))
(SETQ A #:|Store-Var-10336877|))设置CLOS插槽:
CL-USER 88 > (pprint (macroexpand-1 '(setf (slot-value o1 'bar) 'foo)))
(CLOS::SET-SLOT-VALUE O1 'BAR 'FOO)设置列表的第一个元素:
CL-USER 89 > (pprint (macroexpand-1 '(setf (car some-list) 'foo)))
(SYSTEM::%RPLACA SOME-LIST 'FOO)正如您所看到的,它在扩展中使用了大量的内部代码。用户只需编写一个SETF表单,Lisp就可以计算出实际要做什么代码。
由于您可以编写自己的setter,因此只有您的想象力限制了您可能需要在以下通用语法下放置的内容:
发布于 2014-06-07 07:50:14
在你的例子中:
(defun set-23 (place)
(setf place 23))您不能这样做,因为您必须在上下文中使用setf。这将起作用:
(defmacro set-23 (place)
`(setf ,place 23))
CL-USER> (set-23 (aref *foo* 0))
23
CL-USER> *foo*
#(23 NIL NIL NIL NIL)诀窍是,setf‘知道’如何看待它的参数来自于真实的位置,只有有限数量的函数。这些函数被称为可设置函数。
setf是一个宏,要想以您想要的方式使用它,您还必须使用宏。
您没有收到错误的原因是您实际上成功地修改了词法变量place,该变量绑定到复制所选数组元素。
https://stackoverflow.com/questions/24094927
复制相似问题