push不能像我期望的那样工作 - 为什么?

Gol*_*den 3 lisp common-lisp

我遇到了一个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不是函数名称; 尝试使用符号代替

现在我有两个问题:

  1. 为什么这不起作用?我希望push返回列表(2 3 5 7 11),为什么不会发生?我哪里错了?
  2. 除此之外,我没有收到错误消息.Lisp试图告诉我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操作.由于宏无法检测到错误,因此稍后在宏扩展代码中检测到错误.