我遇到了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不是函数名称;尝试使用符号
现在我有两个问题:
push
返回列表(2 3 5 7 11)
,为什么不会发生这种情况?我哪里错了?3 is not a function name
告诉我什么?当然,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)