在常见的lisp函数中包含一个常见的lisp宏

时间:2014-05-04 14:41:15

标签: macros common-lisp

我是普通的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))。你能解释一下发生了什么吗?

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
相关问题