[TOC=2] ## 15.1 简介 本章中,我会讲解如何自定义语法。用户定义语法称作**宏(Macro)**。Lisp/Scheme中的宏比C语言中的宏更加强大。宏可以使你的程序优美而紧凑。 宏是代码的变换。代码在被求值或编译前进行变换,and the procedure continues as if the transformed codes are written from the beginning. 你可以在Scheme中通过用符合R5RS规范的`syntax-rules`轻易地定义简单宏,相比之下,在Common Lisp中自定义语法就复杂多了。使用`syntax-rules`可以直接定义宏而不用担心**变量的捕获(Variable Capture)**。On the other hand, defining complicated macros that cannot be defined using the syntax-rules is more difficult than that of the Common Lisp. ## 15.2 实例:简单宏 我将以一个简单的宏作为例子。 [代码片段 1] 一个将变量赋值为’()的宏 ~~~ (define-syntax nil! (syntax-rules () ((_ x) (set! x '())))) ~~~ `syntax-reuls`的第二个参数由是变换前表达式构成的表。`_`代表宏的名字。简言之,**代码片段1**表示表达式`(nil! x)`会变换为`(set! x '())`. 这类过程不能通过函数来实现,这是因为函数的闭包性质限制它不能影响外部变量。让我们来用函数实现**代码片段1**,并观察效果。 ~~~ (define (f-nil! x) (set! x '())) (define a 1) ;Value: a (f-nil! a) ;Value: 1 a ;Value: 1 ; the value of a dose not change (nil! a) ;Value: 1 a ;Value: () ; a becomes '() ~~~ 我会演示另外一个例子。我们编写宏`when`,其语义为:当谓词求值为真时,求值相应语句。 ~~~ (define-syntax when (syntax-rules () ((_ pred b1 ...) (if pred (begin b1 ...))))) ~~~ **代码片段2**中的`...`代表了任意多个数的表达式(包括0个表达式)。**代码片段2**揭示了诸如表达式`(when pred b1 ...)`会变换为`(if pred (begin b1 ...))`。 由于这个宏是将表达式变换为`if`特殊形式,因此它不能使用函数来实现。下面的例子演示了如何使用`when`。 ~~~ (let ((i 0)) (when (= i 0) (display "i == 0") (newline))) i == 0 ;Unspecified return value ~~~ 我会演示两个实宏:`while`和`for`。只要谓词部分求值为真,`while`就会对语句体求值。而数字在指定的范围中,`for`就会对语句体求值。 ~~~ (define-syntax while (syntax-rules () ((_ pred b1 ...) (let loop () (when pred b1 ... (loop)))))) (define-syntax for (syntax-rules () ((_ (i from to) b1 ...) (let loop((i from)) (when (< i to) b1 ... (loop (1+ i))))))) ~~~ 下面演示了如何实用它们: ~~~ define-syntax while (syntax-rules () ((_ pred b1 ...) (let loop () (when pred b1 ... (loop)))))) (define-syntax for (syntax-rules () ((_ (i from to) b1 ...) (let loop((i from)) (when (< i to) b1 ... (loop (1+ i))))))) ~~~ > 练习1 > > 编写一个宏,其语义为:当谓词求值为假时执行相应的表达式。(语义与`when`相对) ## 15.3 syntax-rule的更多细节 ### 15.3.1 多个定义模式