我是普通的lisp语言的新手。我只有3个月了。有一天,我突然想到,因为它是阻止s表达式被评估的引用,也许我可以通过“驱逐”“引用”来编写我自己的eval。鉴于宏没有评估它的输入,我写这个:
(defmacro my-eval (x)
(car (cdr x)))
但它不起作用!例如,当我写(my-eval '(car '(1 2 3))
时,我认为此宏接收(quote (car (quote 1 2 3)))
作为其输入。
然而,它将其转换为(car (quote (1 2 3))
,然后逐出它,其值为1,但实际上是sbcl1.1.15 print (quote (1 2 3))
。你能解释一下发生了什么吗?
答案 0 :(得分:1)
好的,所以除了它的名字外,我无法看到宏的明显错误。真的你没有引用,没有评估。你没有在这里执行代码(这没关系)你只是删除第一个引用。
我可能会建议您使用宏的这种轻微变化
(defmacro unquote (form)
(if (eq (first form) 'quote)
(second form)
(error "Cannot unquote this form as it is not quoted: ~s" form)))
只是为了一点额外的安全:)
现在回到预期的结果。给出:
(my-eval '(car '(1 2 3))
,正如你所知道的那样实际上是
(my-eval (quote (car (quote (1 2 3)))))
您正在使用cdr
(quote (car (quote (1 2 3))))
((car (quote (1 2 3))))
然后你正在使用cdr
(这是正确的)并且给你
(car (quote (1 2 3)))
到目前为止一切顺利。现在,因为您正在使用宏,宏的结果将被插回到代码中,然后在运行时将执行代码,从而为您提供1
的正确结果。
我将建议您描述的最后一个问题(使用sbcl 1.1.15)可能实际上是用户错误,因为我已经使用旧版本对其进行了测试,但它工作正常。此外,如果有一个错误,那就是那种会在人们的代码中很快显示出来的错误:)
所以TLDR你的宏没有任何问题,虽然它可以做一些额外的安全检查和一个更好的名字。要记住的主要事情是宏在宏扩展时是否已经评估,但它们没有评估它们的参数(除非你强制它们)。宏只返回代替宏表单的代码,并在运行时进行评估。
祝你好运!
答案 1 :(得分:1)
你是如何在SBCL获得(quote (1 2 3))
的。
是的,如果参数确实是'(car '(1 2 3))
,您可以删除引用,但如果您的参数是包含相同评估引用形式的变量,则不能删除引用。例如
(my-eval '(car '(1 2 3))) ; ==> 1
(setf test '(car '(1 2 3))) ; ==> (car '(1 2 3))
(my-eval test) ; ==> FAIL!
为什么它不起作用是因为my-eval
将接收符号test
作为参数而不是符号后面的值。执行(cadr x)
就像执行(cadr 'test)
一样,它会失败。宏在变量具有值之前运行(在宏扩展时间内),因此它们可以帮助减少代码大小,因为它有很多样板,但它不能替换eval
。
答案 2 :(得分:0)
首先,让我们定义宏,查看宏展开,然后查看使用此宏评估表单的结果。然后我们找出为什么每个表单产生它的作用。定义很简单;你已经提供了:
CL-USER> (defmacro my-eval (x)
(car (cdr x)))
MY-EVAL
现在让我们来看看(my-eval '(car '(1 2 3)))
的宏展开是什么:
CL-USER> (macroexpand-1 '(my-eval '(car '(1 2 3))))
(CAR '(1 2 3))
T
这是我们应该期待的。表单'(car '(1 2 3))
是
(quote (car (quote (1 2 3))))
cdr
是
((car (quote (1 2 3))))
car
是
(car (quote (1 2 3)))
这是(my-eval '(car '(1 2 3)))
将被替换的代码。这意味着,当我们评估(my-eval '(car '(1 2 3)))
时,我们应该期望看到相同的结果,就像我们直接评估它一样。当然,(car '(1 2 3))
评估为1
。我们来看看:
CL-USER> (my-eval '(car '(1 2 3)))
1
quote
是Common Lisp中的特殊运算符。它具有(quote object)
返回object
的特殊行为。 object
未获得评估。如果它是一个列表,那么你会得到一个列表;如果它是一个数字你会得到一个数字,等等。但是对象来自哪里?在大多数情况下,您从一个文件或流中读取Lisp代码,读者有责任从文本表示中创建对象。例如,当您编写'53
或(quote 53)
时,您会得到一个数字,因为读者已经从您那里产生了数字53并返回了(list 'quote 53)
(或{{{ 1}})。当评估者收到该表单时,它会识别出它是列表,并且该列表的(list 'quote '53)
是符号car
,因此调用{{1}命名的特殊运算符。 1}}使用参数作为列表的第二个对象。这是编码到系统中的特殊行为。
quote
- 形式实现为宏,那么它需要做什么?它需要转
quote
进入表单,保证评估为quote
。您不能简单地扩展到(kwote object)
,因为您不希望将对象传递给求值程序。如果它可以被视为另一种形式,评估者将尝试对其进行更多评估。您可以传递给评估者以保证获得object
的唯一内容是
object
这意味着如果您想实现自己的报价,则必须使用te内置报价。例如,
object