具有语法规则的卫生和引用透明的宏
LISP 和 Scheme 相比其他主流编程语言的最大优势是它们的宏系统。与 C 预处理器和其他宏语言不同,Scheme 宏将解析后的代码作为输入,并将扩展代码作为输出返回。这是 Scheme 的代码是数据短语的应用之一,它使语言如此强大。
Scheme 中的宏是用 define-syntax
创建的,define-syntax
可以通过多种方式定义宏。最简单的方法是使用 syntax-rules
,它使用模式匹配将输入代码转换为输出代码。
此示例创建一个简单的 for item in list
和 for list as item
语法,用于循环列表中的元素:
(define-syntax for
(syntax-rules (in as) ; 'in' and 'as' keywords must match in the pattern
; When the 'for' macro is called, try matching this pattern
((for element in list
body ...) ; Match one or more body expressions
; Transform the input code
(for-each (lambda (element)
body ...)
list))
; Try matching another pattern if the first fails
((for list as element
body ...)
; Use the existing macro for the transform
(for element in list
body ...))))
然后可以按如下方式使用这两个宏,从而提供更强制的风格:
(let ((names '(Alice Bob Eve)))
(for name in names
(display "Hello ")
(display name)
(newline))
(for names as name
(display "name: ")
(display name)
(newline)))
运行代码将提供预期的输出:
Hello Alice
Hello Bob
Hello Eve
name: Alice
name: Bob
name: Eve
需要注意的最常见错误是没有将正确的值传递给宏,这通常会导致无效的错误消息应用于扩展形式而不是宏调用。
上面的 for
语法定义不检查它们是否传递了标识符和列表,因此传递任何其他类型将导致指向 for-each
调用而不是 for
调用的错误。调试这会破坏宏的目的,因此由用户将检查放在那里并报告使用错误,然后可以在编译时捕获。