我遇到了一个push
我没有得到的功能行为.也许有人可以向我解释为什么Lisp会这样做.
假设我将列表定义为全局变量,然后尝试使用以下代码将新值推送到它:
(defparameter *primes* '(3 5 7 11))
(push 2 *primes*)
Run Code Online (Sandbox Code Playgroud)
那*primes*
就是现在(2 3 5 7 11)
.到现在为止还挺好.
现在我尝试做同样的事情,但没有*primes*
变量,即:
(push 2 '(3 5 7 11))
Run Code Online (Sandbox Code Playgroud)
结果是一条错误消息:
EVAL:3不是函数名称; 尝试使用符号代替
现在我有两个问题:
push
返回列表(2 3 5 7 11)
,为什么不会发生?我哪里错了?3 is not a function name
什么?当然,3
不是函数名,但我不会尝试调用3
任何名字的函数,是吗?任何帮助表示赞赏:-)
Rai*_*wig 16
如果您阅读了CL Hyperspec for PUSH,您会看到push
需要一个地方.
一个地方就像一个变量,一个结构槽,一个类槽,一个数组访问或类似的东西.由于Lisp使用链接的cons单元格作为列表,因此在cons单元格前面推送一些东西是没有意义的,没有参考.
所以上面很简单:我们不能推到直接列表.
为何出现此错误消息?
这有点复杂......
(push 2 '(3 5 7 11))
Run Code Online (Sandbox Code Playgroud)
实际上是:
(push 2 (quote (3 5 7 11))
Run Code Online (Sandbox Code Playgroud)
函数可以是一个地方,然后它需要一个相应的setter函数.这里的setter被认为是(setf quote)
- 这是正确的,Common Lisp有时可以将列表作为函数名,而不仅仅是符号.
如果我们看一下上面的宏观扩展:
? (pprint (macroexpand '(push 2 (quote (3 5 7 11)))))
(LET* ((#:G328 2) (#:G327 (3 5 7 11)) (#:G326 (CONS #:G328 '#:G327)))
#:G327
#:G326
(FUNCALL #'(SETF QUOTE) #:G326 #:G327))
Run Code Online (Sandbox Code Playgroud)
你可以看到它试图调用setter.但它也认为这(3 5 7 11)
是一种Lisp形式.
我举一个例子,它实际上有效,但我们不使用quote
,而是一个真正的访问函数:
CL-USER 40 > (let ((v (vector (list (list 'a 'b 'c) (list 'd 'e 'f))
(list (list 1 2 3) (list 4 5 6)))))
(print v)
(push 42 (first (aref v 1)))
(print v)
(values))
#(((A B C) (D E F)) ((1 2 3) (4 5 6)))
#(((A B C) (D E F)) ((42 1 2 3) (4 5 6)))
Run Code Online (Sandbox Code Playgroud)
在上面first
是getter和CL知道相应的setter.表单(aref v 1)
是调用并返回向量的索引1元素.然后我们推送到元素的第一个列表.
您的通话具有类似的结构,(3 5 7 11)
并且处于类似的位置(aref v 1)
.Lisp系统说在(3 4 7 11)
那时数字3
不是一个有效的函数.哪个是对的.但真正的错误是关于push
操作.由于宏无法检测到错误,因此稍后在宏扩展代码中检测到错误.