McCarthy's基本 S-Function 和谓词是 atom
、eq
、car
、cdr
、cons
然后他继续添加他的基本符号,以编写他所谓的 S-函数:quote
、cond
、lambda
, 标签
在此基础上,我们将称这些为“LISP 原语”(尽管我愿意接受有关类型谓词的争论,例如 numberp
)
您将如何在您选择的 LISP 中仅使用这些原语来定义 defmacro
函数? (包括 Scheme 和 Clojure)
最佳答案
在像 McCarthy 的 LISP 机器这样的机器上尝试这样做的问题是没有办法在运行时阻止参数评估,也没有办法在编译时改变周围的东西(这是宏的作用:他们在编译之前重新排列代码,基本上)。
但这并不能阻止我们在运行时在 McCarthy 的机器上重写我们的代码。诀窍是引用我们传递给“宏”的参数,这样它们就不会被评估。
例如,让我们看一下我们可能想要的功能; 除非
。我们的理论函数有两个参数,p
和 q
,并返回 q
unless p
是真的。如果 p
为真,则返回 nil。
一些示例(在 Clojure 的语法中,但这不会改变任何东西):
(unless (= "apples" "oranges") "bacon")
=> "bacon"
(unless (= "pears" "pears") "bacon")
=> nil
所以一开始我们可能想把 unless
写成一个函数:
(defn unless [p q]
(cond p nil
true q))
这似乎工作得很好:
(unless true 6)
=> nil
(unless false 6)
=> 6
如果使用 McCarthy 的 LISP,它会工作得很好。问题是在我们现代的 Lisp 中,我们不仅拥有无副作用的代码,因此无论我们是否希望对传递给 unless
的所有参数进行求值,这一事实都是有问题的。事实上,即使在 McCarthy 的 LISP 中,如果评估其中一个参数需要花费 ages 时间,而我们只是想很少这样做,这也可能是一个问题。但这尤其是一个副作用问题。
所以我们希望我们的 unless
评估并返回 q
仅当 p
为假时。如果我们将 q
和 p
作为参数传递给一个函数,我们就不能这样做。
但是我们可以在将它们传递给我们的函数之前引用
它们,以防止它们被评估。我们可以使用 eval
的强大功能(也已定义,仅使用原语和引用论文后面用原语定义的其他函数)在需要时评估我们需要什么。
所以我们有一个新的unless
:
(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
或其他语言中的等价物。那是因为在 McCarthy 的机器上,没有办法在编译时执行代码。如果您使用 eval
函数评估您的代码,它就不知道不评估“宏”函数的参数。阅读和评估之间没有像现在这样的区别,尽管这个想法是存在的。 “重写”代码的能力存在于 quote
和列表操作与 eval
的结合中,但它并没有像现在是了(我几乎称之为句法糖:只要引用你的论点,你就拥有了宏系统的力量。)
我希望我已经回答了你的问题,而没有尝试自己用这些原语定义一个像样的 defmacro
。如果你真的想看到那个,我会告诉你难以理解的 source for defmacro
在 Clojure 源代码中,或 Google 周围更多。
关于macros - 仅使用 LISP 原语定义 defmacro 函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3535954/