阅读 Paul Graham's essays关于编程语言,人们会认为 Lisp macros是唯一的出路。作为一名忙碌的开发人员,在其他平台上工作,我还没有使用 Lisp 宏的特权。作为想要了解动态的人,请解释是什么让这个功能如此强大。
还请将此与我从 Python、Java、C# 或 C 开发领域理解的内容联系起来。
最佳答案
为了给出简短的答案,宏用于定义 Common Lisp 或领域特定语言 (DSL) 的语言语法扩展。这些语言直接嵌入到现有的 Lisp 代码中。现在,DSL 的语法可以类似于 Lisp(例如 Peter Norvig 的 Prolog Interpreter 用于 Common Lisp)或完全不同(例如 Infix Notation Math 用于 Clojure)。
这是一个更具体的例子:
Python 在语言中内置了列表推导式。这给出了常见情况的简单语法。线路
divisibleByTwo = [x for x in range(10) if x % 2 == 0]
生成一个列表,其中包含 0 到 9 之间的所有偶数。回到 Python 1.5天没有这样的语法;你会使用更像这样的东西:
divisibleByTwo = []
for x in range( 10 ):
if x % 2 == 0:
divisibleByTwo.append( x )
它们在功能上是等价的。让我们暂停怀疑并假装 Lisp 有一个非常有限的循环宏,它只做迭代,没有简单的方法来做等同于列表理解。
在 Lisp 中,您可以编写以下内容。我应该注意到,这个人为的例子被选为与 Python 代码相同,而不是 Lisp 代码的好例子。
;; the following two functions just make equivalent of Python's range function
;; you can safely ignore them unless you are running this code
(defun range-helper (x)
(if (= x 0)
(list x)
(cons x (range-helper (- x 1)))))
(defun range (x)
(reverse (range-helper (- x 1))))
;; equivalent to the python example:
;; define a variable
(defvar divisibleByTwo nil)
;; loop from 0 upto and including 9
(loop for x in (range 10)
;; test for divisibility by two
if (= (mod x 2) 0)
;; append to the list
do (setq divisibleByTwo (append divisibleByTwo (list x))))
在继续之前,我应该更好地解释一下什么是宏。它是 by 代码对代码执行的转换。也就是说,由解释器(或编译器)读取的一段代码,它将代码作为参数,进行操作并返回结果,然后就地运行。
当然,这需要大量的输入,而程序员又很懒惰。所以我们可以定义 DSL 来进行列表理解。事实上,我们已经在使用一个宏(循环宏)。
Lisp 定义了一些特殊的语法形式。引号 ('
) 表示下一个标记是文字。准引号或反引号 (`
) 指示下一个标记是带有转义符的文字。转义由逗号运算符指示。文字 '(1 2 3)
相当于 Python 的 [1, 2, 3]
。您可以将它分配给另一个变量或就地使用它。您可以将 `(1 2 ,x)
视为 Python 的 [1, 2, x]
的等价物,其中 x
之前是一个变量定义。这个列表符号是进入宏的魔法的一部分。第二部分是 Lisp 阅读器,它智能地用宏替换代码,但最好的说明如下:
因此我们可以定义一个名为lcomp
(列表理解的缩写)的宏。它的语法将与我们在示例中使用的 python 完全一样 [x for x in range(10) if x % 2 == 0]
- (lcomp x for x in (range 10) 如果 (= (% x 2) 0))
(defmacro lcomp (expression for var in list conditional conditional-test)
;; create a unique variable name for the result
(let ((result (gensym)))
;; the arguments are really code so we can substitute them
;; store nil in the unique variable name generated above
`(let ((,result nil))
;; var is a variable name
;; list is the list literal we are suppose to iterate over
(loop for ,var in ,list
;; conditional is if or unless
;; conditional-test is (= (mod x 2) 0) in our examples
,conditional ,conditional-test
;; and this is the action from the earlier lisp example
;; result = result + [x] in python
do (setq ,result (append ,result (list ,expression))))
;; return the result
,result)))
现在我们可以在命令行执行:
CL-USER> (lcomp x for x in (range 10) if (= (mod x 2) 0))
(0 2 4 6 8)
非常整洁,是吧?现在它不止于此。如果你愿意,你有一个机制,或者一个画笔。您可以使用任何您可能想要的语法。类似于 Python 或 C# 的 with
语法。或者 .NET 的 LINQ 语法。最后,这就是 Lisp 吸引人们的地方 - 极致的灵 active 。
关于macros - 是什么让 Lisp 宏如此特别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/267862/