我最近开始学习lisp。我正在阅读Land of Lisp一书中的例子,我觉得在第7章到达下面的例子之前,我对一切都很了解:
(defun quote-it (x)
(list 'quote x))
现在,我知道list
会创建一个带有参数的列表,就像在(list 1 2 3 4)
中评估列表(1 2 3 4)
一样。
我也知道quote
允许我引用一个论点,就像我可以用'
做的那样。因此'east
与(quote east)
现在,有趣的是,上面的函数不返回列表,而只是引用我传递给它的任何内容。所以,如果我称之为
(quote-it east)
它只会返回'east
而不是('east)
如果我这样做了,我会把这个函数写成
(defun quote-it (x)
(quote x))
所以,我不知道为什么我们在本书的例子中将'quote
中的命令写为(list 'quote x)
。
我知道我可以使用引号来切换代码和数据,例如'(+ 1 2)
,但在这种情况下,我的意图是在这里实际应用引用函数。那么,为什么(list 'quote x)
?
那么,有更多经验的人可以澄清一下吗?
答案 0 :(得分:6)
Lisp源代码由Lisp编程中使用的一些相同数据结构表示。这里特别重要的是列表和符号。当Lisp评估具有
形式的表单时(quote something)
也就是说,当Lisp评估一个列表并且其第一个元素是符号quote
的表单时,它返回对象something
而不进行评估 。因此
(quote 2) ;=> 2
(quote (a b c)) ;=> (a b c)
现在,这就是评估者(或编译器,& c。)在得到一个Lisp对象进行评估时所做的事情恰好是一个列表,其第一个元素是符号{{1} }。作为Lisp 程序员,我们仍然必须编写代码,以便Lisp 阅读器阅读并传递给evalutor。我们可以写长格式
quote
在我们的源代码中,Lisp阅读器将读取它们并将它们传递给编译器,但我们是virtuous programmers,所以我们很懒,并且想要避免一些打字。所以,我们可以输入
(quote 2)
(quote (a b c))
代替。 编译器最终获得完全相同的输入;一个列表,其第一个元素是符号'2
'(a b c)
,其第二个值是quote
或2
。
现在我们可以谈谈您的代码了。表格
(a b c)
返回一个列表,其第一个元素是符号(list 'quote x)
,其第二个元素是变量quote
的值。可以打印作为
x
现在,Lisp打印机有点聪明,并且可以选择使用我们在编写源代码时允许使用的相同速记打印类似的东西。所以也可以打印为
(quote <value-of-x>) ; fill in x's value for <value-of-x>, of course
现在我们已经足够解决有关该书代码的问题:
现在,有趣的是,上面的函数不会返回列表,但是 简单地引用我传递给它的任何东西。所以,如果我称之为
'<value-of-x>
它只会返回(quote-it east)
而不是'east
由于('east)
是'east
的缩写,我们现在看到(quote east)
确实,实际上会返回一个列表。它是您期望的表单列表:列表的第一个元素是符号(quote-it east)
,第二个元素是符号quote
。如果已返回east
,它仍将返回一个列表,但它将返回错误表单的列表。当我们扩展缩写时,('east)
是列表('east)
;也就是说,它是单个元素的列表,该元素是另一个列表,其第一个元素是符号((quote east))
,第二个元素是符号quote
。它肯定是一个列表,但它不是你要找的列表。
现在我们可以看看您提出的代码。您的函数east
仅在您正在调用quote-it
的情况下偶然发生。也就是说,你可以做到
(quote-it 'x)
但请注意,您正在获取符号,并且您将获得符号(quote-it 'x)
;=> x
。您不获取一个列表,其第一个元素是符号x
,其第二个元素是quote
。当您将<value-of-x>
与其他值一起使用时,您会看到问题:
quote-it
你仍然得到一个符号,因为
(quote-it 2)
;=> x
采用参数,将其绑定到词法变量(defun quote-it (x)
(quote x)) ; or 'x
,然后返回x
的值。 (quote x)
的价值是多少?如前所述,当评估者(或编译器,&amp; c。)获得符号(quote x)
和quote
的列表时,该值为文字something
。由于something
的正文是符号quote-it
和符号quote
的列表,因此x
正文的值是符号quote-it
。< / p>
答案 1 :(得分:4)
示例:如果要构建带有符号foo
和变量号的列表:
(foo <some-number>)
现在我们为它编写一个函数:
(defun foo-it (n)
(list 'foo n))
显然
(defun foo-it-1 (n)
(foo n))
赢了工作。它会在FOO
上调用函数n
,而不是构建列表。
(defun foo-it-2 (n)
'(foo n))
上面也行不通,因为列表(foo n)
没有被评估并按原样返回。
现在Lisp有一个反引号语法:
(defun foo-it-3 (n)
`(foo ,n))
以上将有效。逗号表示将评估符号n
。
为了你的问题。将foo
替换为quote
:
(list 'foo n) -> (list 'quote n)
或
`(foo ,n) -> `(quote ,n)
主要区别在于Lisp打印机始终将(foo 3)
打印为(foo 3)
。 FOO
没有特殊目的。但是QUOTE
有(因为它是在语言中定义的)而且Lisp打印机可能会将(quote 3)
打印为'3
。
示例:
CL-USER 14 > (progn
(write (list 'foo 3) :pretty nil)
(terpri)
(write (list 'foo 3) :pretty t)
(values))
(FOO 3)
(FOO 3)
CL-USER 15 > (progn
(write (list 'quote 3) :pretty nil)
(terpri)
(write (list 'quote 3) :pretty t)
(values))
(QUOTE 3)
'3
那只是因为在Lisp中,quote
字符是定义的内置语法,打印机知道它。
另请注意,打印也会被一组特殊符号修改,例如*print-pretty*
,*print-readably*
,......因此,根据这些变量的设置,不同的Lisp设置可能会打印出不同的颜色。< / p>
答案 2 :(得分:2)
Lisp有列表,例如:(玛丽扔球)。
有些列表描述了一个计算,如:(* pi(+ r r))
第一个列表是数据,第二个是适合评估者咀嚼的表达式。
但是我们需要一种在表达式中包含数据的方法。几十年前,我们引入了一个特殊的操作员来实现这一点,例如:(伯爵(引用(玛丽扔球)))
时间过去了,开发人员厌倦了打字(引用......),所以他们发明了一个简短的符号:(伯爵 - &#39;(玛丽扔球))。
这些天,速记是在读取时实现的,因为字符是从文件中读出的,或者是从终端读出的。这是通过一个名为&#34;读取器宏的工具完成的。&#34;这个名字让大多数初学者感到困惑,因为语言中还有另一个名为&#34; macros&#34;这与读取文件几乎没什么关系。它们通常用于添加少许语法糖。如在这个引用示例中。在读取文件(或终端)流之后,所有内容都只是一个列表。
你会注意到如果你输入:(引用bob)进入你的lisp提示符,你会回来:&#39; bob。打印机有助于了解(引用bob)具有简写形式的约定。如果您决定编写自己的阅读器宏,则可能需要教授打印机如何进行播放。
宏,如v.s.读者宏,让您介绍自己的特殊运算符。这使您可以将自定义域特定语言嵌入到代码中。所以 - 有些 - 宏可以让你扩展语义和读者宏,让你扩展语法。宏会影响编译器和求值程序的行为。
答案 3 :(得分:0)
你对读者宏感到困惑' Lisp的顶级是read / eval / print 如果你读'(foo bar)你得到(引用(foo bar)) 如果你eval(引用(foo bar))你得到(foo bar)