我遇到了一个我没有得到的 push
函数的行为。也许有人可以向我解释为什么 Lisp 会这样。
假设我将一个列表定义为全局变量,然后尝试使用以下代码向其推送一个新值:
(defparameter *primes* '(3 5 7 11))
(push 2 *primes*)
那么 *primes*
现在是 (2 3 5 7 11)
。到目前为止,还不错。
现在我尝试做同样的事情,但没有 *primes*
变量,即:
(push 2 '(3 5 7 11))
结果是一条错误信息:
EVAL: 3 is not a function name; try using a symbol instead
现在我有两个问题:
- 为什么这不起作用?我希望
push
返回列表(2 3 5 7 11)
,为什么这没有发生?我哪里错了? - 除此之外,我没有收到错误消息。 Lisp 试图用
3 is not a function name
告诉我什么?当然,3
不是函数名,但我不会尝试在任何地方调用名为3
的函数,对吗?
感谢任何帮助:-)
最佳答案
如果您阅读 PUSH 的 CL Hyperspec ,您会读到 push
需要一个位置。
place 类似于变量、结构槽、类槽、数组访问或类似的东西。由于 Lisp 对列表使用链接的 cons 单元,因此在没有引用的情况下将某些东西推到 cons 单元前面是没有意义的。
所以上面很简单:我们不能推送到直接列表。
为什么会出现此错误消息?
这有点复杂...
(push 2 '(3 5 7 11))
实际上是:
(push 2 (quote (3 5 7 11))
一个函数可以是一个地方,那么它就需要一个对应的setter函数。这里的 setter 被认为是 (setf quote)
- 没错,Common Lisp 有时可以将 list 作为函数名称,而不仅仅是符号。
如果我们看一下上面的宏展开:
? (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))
您可以看到它尝试调用 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)))
在上面first
是getter,CL 知道相应的setter。形式 (aref v 1)
是调用并返回向量的索引 1 元素。然后我们将推送到元素的 first 列表。
您的调用具有相似的结构,(3 5 7 11)
与 (aref v 1)
处于相似的位置。 Lisp 系统表示在 (3 4 7 11)
中,数字 3
不是有效函数。哪个是对的。但真正的错误是关于 push
操作。由于宏无法检测到错误,因此稍后会在宏扩展代码中检测到错误。
关于lisp - push 没有像我预期的那样工作 - 为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23983629/