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

时间:2014-06-01 19:30:39

标签: lisp common-lisp

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

现在我有两个问题:

  1. 为什么这不起作用?我希望push返回列表(2 3 5 7 11),为什么不会发生这种情况?我哪里错了?
  2. 除此之外,我没有收到错误消息。 Lisp试图用3 is not a function name告诉我什么?当然,3不是函数名,但我不会尝试在任何地方调用名为3的函数,是吗?
  3. 感谢任何帮助: - )

3 个答案:

答案 0 :(得分:16)

如果您阅读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有时可以将列表作为函数名,而不仅仅是符号。

如果我们看一下上面的宏观扩张:

? (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元素。然后我们推进元素的第一个列表。

您的通话具有类似的结构,(3 5 7 11)处于与(aref v 1)类似的位置。 Lisp系统表示在(3 4 7 11)中,数字3不是有效函数。哪个是对的。但真正的错误是关于push操作。由于宏无法检测到错误,因此稍后会在宏扩展代码中检测到错误。

答案 1 :(得分:1)

我只找到了emacs lisp手册push,但我猜它的行为类似于Common Lisp

  

- Macro:推送元素列表名称

     

此宏创建一个新列表,其car是元素,其cdr是listname指定的列表,将该列表保存在listname 中。

所以似乎push正在修改其参数listname,这对于文字列表是不可能的。要做到你的想法,可以使用cons代替。

对于第二部分3 is not a function name,我会说push或其中的某个函数会尝试评估文字列表。评估(3 5 7 11)表示使用参数3调用函数5 7 11。因此错误信息。


再次来自emacs, Ctrl-h f push

  

push是`cl.el'中的一个Lisp宏。

     

(推送X PLACE)

     

将X插入PLACE中存储的列表的开头   类似于(setf PLACE(cons X PLACE)),虽然更加小心   仅以正确的顺序评估每个参数一次。 PLACE可以   是一个符号,或`setf'允许的任何广义变量。

setf反过来允许place成为

  

符号引用,例如(car x)或(aref x i)

解释了为什么push评估第二个参数。

答案 2 :(得分:-1)

我认为在第二种情况下你需要CONS

(cons 2 '(3 5 7 11)) => (2 3 5 7 11)