[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?多個定義模式