Macros 仅使用LISP原语定义defmacro函数?

Macros 仅使用LISP原语定义defmacro函数?,macros,clojure,lisp,scheme,primitive,Macros,Clojure,Lisp,Scheme,Primitive,基本的S-函数和谓词是atom,eq,car,cdr,cons 然后,他继续添加到他的基本符号中,以便能够编写他所谓的S函数:quote,cond,lambda,label 在此基础上,我们将这些称为“LISP原语”(尽管我对类型谓词持开放态度,如numberp) 如何在您选择的LISP中仅使用这些原语来定义defmacro函数?(包括Scheme和Clojure)完整地解释它的所有细节需要大量的空间和时间才能在这里找到答案,但提纲非常简单。每个LISP的核心最终都有一个类似于READ-EVAL

基本的S-函数和谓词是
atom
eq
car
cdr
cons

然后,他继续添加到他的基本符号中,以便能够编写他所谓的S函数:
quote
cond
lambda
label

在此基础上,我们将这些称为“LISP原语”(尽管我对类型谓词持开放态度,如
numberp


如何在您选择的LISP中仅使用这些原语来定义
defmacro
函数?(包括Scheme和Clojure)

完整地解释它的所有细节需要大量的空间和时间才能在这里找到答案,但提纲非常简单。每个LISP的核心最终都有一个类似于READ-EVAL-PRINT循环的东西,也就是说,一个元素一个元素地获取一个列表,解释它,并改变状态——要么在内存中,要么通过打印结果

读取部分查看每个读取的元素,并对其执行操作:

(cond ((atom elem)(lambda ...))
      ((function-p elem) (lambda ...)))
要解释宏,只需(?)实现一个函数,将宏的模板文本放在存储中的某个位置,一个repl循环的谓词——这意味着只需定义一个函数——它说“哦,这是一个宏!”,然后将模板文本复制回读卡器中,以便对其进行解释


如果你真的想看到毛茸茸的细节,可以阅读计算机程序的结构和解释,或者阅读奎因内克的

在McCarthy的LISP机器这样的机器上尝试这样做的问题是,没有办法防止在运行时进行参数计算,也没有办法在编译时改变情况(这就是宏所做的:它们基本上会在编译之前重新排列代码)

但这并不能阻止我们在麦卡锡的机器上运行时重写代码。诀窍是引用我们传递给“宏”的参数,这样它们就不会被计算

举个例子,让我们看一个我们可能想要的函数<代码>除非。我们的理论函数接受两个参数,
p
q
,并返回
q
,除非
p
为真。如果
p
为真,则返回nil

一些示例(在Clojure的语法中,但这不会改变任何内容):

因此,首先我们可能希望编写
,除非将
作为函数:

(defn unless [p q]
    (cond p nil
          true q))
这似乎很管用:

(unless true 6)
=> nil

(unless false 6)
=> 6
有了麦卡锡的口齿不清,效果会很好。问题是,在现代的Lisp中,我们不仅仅有无副作用的代码,因此,除非对
求值,否则传递给
的所有参数(无论我们是否希望它们求值)都是有问题的。事实上,即使在麦卡锡的口齿不清中,如果,比如说,评估其中一个论点需要花费很长时间,而我们只想很少这样做,这也可能是一个问题。但这是一个特别有副作用的问题

因此,我们希望我们的
除非
p
为false时计算并返回
q
。如果我们将
q
p
作为参数传递给函数,我们就无法做到这一点

但是我们可以在将它们传递给我们的函数之前引用它们,从而阻止对它们的评估。我们可以使用
eval
(也定义了,只使用原语和在参考文献后面用原语定义的其他函数)的功能,在需要时评估我们需要什么

所以我们有了一个新的
,除非

(defn unless [p q] 
    (cond (eval p) nil 
          true (eval q)))
我们使用它有点不同:

(unless (quote false) (quote (println "squid!")))
=> "squid" nil
(unless (quote true) (quote (println "squid!")))
=> nil
这里有一个可以称之为宏的东西


但这不是
defmacro
或其他语言中的等效项。这是因为在麦卡锡的机器上,没有一种在编译时执行代码的方法。如果您使用
eval
函数评估代码,它就不知道不评估“宏”函数的参数。阅读和评价之间没有现在这样的区别,尽管有这样的想法。“重写”代码的能力就在那里,在
quote
和与
eval
相结合的列表操作的凉爽中,但它并没有像现在这样在语言中实习(我几乎称之为语法糖:只要引用你的参数,你就有了宏系统的力量。)


我希望我已经回答了你的问题,而不是试图用这些原语定义一个像样的
defmacro
。如果你真的想看到这一点,我会告诉你在Clojure的源代码中很难找到,或者谷歌搜索更多

Defmacro
本身是一个宏,而不是一个函数。@Issac Hodes:再次检查,Clojure中的Defmacro是一个宏,您缺少了一些东西。紧跟在(def…)结尾之后看一看。Lisp中的每个宏只是一个绑定到lambda的符号,在某个地方设置了一个小标志,eval会进行检查,如果设置了,会导致eval在宏展开时调用lambda,并用其返回值替换表单。如果你看一下defmacro宏本身,你会发现它所做的一切都是重新安排的,这样你就可以得到一个变量的def,将一个fn作为它的值,然后在这个变量上调用.setMacro,就像core.clj在defmacro本身上手动做的一样,因为它还没有defmacro来定义defmacro。对,这是有道理的——我想这意味着我应该把它看作一个宏。我想我是从错误的角度来看的!谢谢你让我上了火车:)SCICP对宏有深入的了解吗?通过快速搜索,我在PDF中找不到太多关于它们的内容。这可能过于简单了,但你是说如果我编写自己的eval函数,只要我的eval函数知道宏声明是什么,以及该宏的后续调用是什么,我就可以完成80%的工作。如果你想让宏的语义与CL/Clojure匹配,<代码
(unless (quote false) (quote (println "squid!")))
=> "squid" nil
(unless (quote true) (quote (println "squid!")))
=> nil