这是我的代码:
(format t "~a~%" (list 1 2 3 4))
(format t "~a~%" '(1 2 3 4))
(format t "~a~%" (remove-if-not #'evenp (list 1 2 3 4)))
(format t "~a~%" (remove-if-not #'evenp '(1 2 3 4)))
这是输出:
$ clisp bar.lisp
(1 2 3 4)
(1 2 3 4)
(2 4)
(2 4)
我的问题:
(list 1 2 3 4)
和'(1 2 3 4)
?答案 0 :(得分:3)
似乎返回绝对相同结果'(1 2 3 4)
的{{1}}和(list 1 2 3 4)
之间的差异是不可见的,因为Lisp中的数字会对自己进行评估。
而不是数字采用符号或表达式:
(1 2 3 4)
现在你清楚地看到了差异:(list (+ 1 2) (+ 2 3) (+ 3 4) (+ 4 5))
;; returns/evaluates to:
(3 5 7 9) ;; each of the arguments in a list gets evaluated.
;; while:
'((+ 1 2) (+ 2 3) (+ 3 4) (+ 4 5)) ;; equals to: (quote ((+ 1 2) (+ 2 3) (+ 3 4) (+ 4 5)))
;; returns/evaluates to:
((+ 1 2) (+ 2 3) (+ 3 4) (+ 4 5)) ;; arguments of list not evaluated
;; since `quote` means: take the argument as data - unevaluated.
扩展到(因此是语法糖)'(1 2 3 4)
是特殊形式,而(quote (1 2 3 4))
是的功能即可。 Lisp中的函数评估每个参数。 但是特殊表单不会评估每个参数。对于(list 1 2 3 4)
,quote
的参数不会被评估。
现在您看到,quote
,a
,b
,c
之前未定义,
d
有效,因为没有评估b c d!
但是这个:
'(a b c d)
;; returns: (A B C D)
导致错误:
(list a b c d)
因为*** - SYSTEM::READ-EVAL-PRINT: variable A has no value
The following restarts are available:
USE-VALUE :R1 Input a value to be used instead of A.
STORE-VALUE :R2 Input a new value for A.
ABORT :R3 Abort main loop
,a
,b
和c
都未定义,但Lisp解释器会尝试评估它们,因为它们是函数参数。
但是你仍然可以通过引用每个参数来使用函数d
获得(A B C D)
- 从而使它们评估它们的符号名而不是Lisp解释器来查找它们未定义的值:
list
注意:有趣的是,不是在所有语言中,函数参数都会被评估,因为它们是函数参数。虽然Python在输入和评估函数体之前评估所有类似于Lisp 的函数参数,但R不会。
因此,函数参数的评估是一种特定于Lisp的方法来处理它的函数参数。 (但它与大多数其他语言共享 - 我只想说函数参数的评估不是一般规则)。这也是Lisp宏(和特殊形式)和Lisp函数之间的根本区别。在Lisp宏(和特殊形式)中,您可以指定在宏体中评估哪些函数参数,哪些不在。这样您就可以完全控制任何宏参数的评估。在Lisp函数中,默认情况下,在进入函数体之前首先计算所有参数。
这也是你在Lisp中学习宏(指定特殊形式)的原因之一。 (list 'a 'b 'c 'd)
;; now it works - though a b c d are not defined yet:
(A B C D)
与quote
一起实际上是所有宏的母亲。任何list
和backquote
操作都可以由unquote
和quote
表示(尽管对于人类读者来说看起来更糟糕)。你可以想象,人们可以长时间思考这个话题。而你的问题直接涉及使Lisp如此迷人的内心和本质。
答案 1 :(得分:1)
不同之处在于创建列表的时间。一个由 reader 创建,另一个在运行时创建。
读者负责将您的文本文件转换为代表代码的数据结构。这个数据结构恰好是列表;这是Lisp(LISt Processing)的想法。
当读者阅读文本 (foo bar)
时,它会创建一个包含两个元素的列表,即符号foo
和bar
。然后编译,i。即已转换为函数调用(或其他调用,但不允许在此处转移),其中foo
命名的函数使用bar
命名的变量的值进行调用。 / p>
有一个特殊的运算符告诉编译器不执行此转换:quote
。当编译器遇到列表(由阅读器构建)(quote (foo bar))
时,它会使用quote
字面上的“受保护”,i。即正好由读者构建的列表(foo bar)
。
撇号'
是quote
的简写,因此'(foo bar)
读为(quote (foo bar))
。
因此,表单'(1 2 3)
正是您在文本文件中写入的列表,正如读者所读到的那样。这称为文字列表。
另一方面,(list 1 2 3)
是具有三个参数的函数list
的正常函数调用。只要在运行时执行此代码,它就会创建这些参数的新列表。
只有在文字列表不变的情况下才应使用它。例如,在创建多维数组时,您可能知道它的大小始终相同(可能是问题的内在因素),因此您编写(make-array '(3 5))
。如果您想使其可配置,请对其进行参数化:(make-array (list width height))
。
您绝对必须避免的一件事是修改文字数据。