Scheme宏基础入门(转载)
Scheme的宏比Lisp的宏简单,但是它有些看起来奇怪的“语法”却很少有文章进行过解释或者文章说了这点却容易忽略,使得我以前对Scheme宏的学习一直摸不到头脑,在看了很多篇有关Scheme宏的介绍文章后,觉得这篇文章写的是最容易理解的了,虽然不能算浅显易懂,有可能宏这个东西说得浅显了就不太容易懂。原文地址:Syntax宏 · 大专栏 (dazhuanlan.com) 。另外一篇Scheme官方介绍宏使用的文章链接:Syntactic Extension (scheme.com)
正文开始---------
宏 是 用户自定义的语法 ,而 Lisp/Scheme 提供的宏远比其他编程语言要强大的多。 使用宏可以让代码漂亮和紧凑
本质上来说宏就是一种 代码转换器 : 代码在被解释或编译前被转换成另外一种形式去执行
在 Scheme 语言中, 在 R5R5 规范以后简单的宏可以被方便地使用 syntax-rules 形式来定义,作为对比 Common Lisp 的宏显得要复杂许多:
- 使用 syntax-rules 可以更 直接 地定义宏,而不需要考虑诸如 变量捕捉 等细节
- 定义复杂的宏,使用 syntax-rules 比起 Common Lisp 的宏来说会困难得多(某些 Scheme 实现提供了 define-macro )
简单宏
把某个变量赋值为 '()
(define-syntax nil!
(syntax-rules ()
;; 转换前和转换后的列表
((_ x) ;; 转换前的代码,_ 表示 宏的名字
(set! x '())))) ;; 转换后的代码
;; (define a 1)
;; a ; => 1
;; (nil! a)
;; a ; => ()
syntax-rules 的 第二个参数 是一个两个元素的 列表 :
- 第一个元素:转换前的代码,其中 _ 代表宏的名字
- 第二个元素:转换后的代码
注意:如果把上面的代码可以写成函数,但是因为闭包的原因,传递进去的参数实际上是不会改变的
(define (f-nil! x) (set! x '())) ;; (define a 1) ;; a ; => 1 ;; (f-nil! a) ; => () ;; a ; => 1 ;; (set! a '()) ;; a ; => ()
当谓词为真的时候,对接下来的表达式求值:
(define-syntax when
(syntax-rules ()
((_ pred b1 ...) ; ... 含义是任意个表达式,可以是0个
(if pred (begin b1 ...)))))
;; (let ((i 0))
;; (when (= i 0)
;; (display "i == 0")
;; (newline)))
;; => i == 0
;; ;Unspecified return value
上面的代码无法用函数来写,因为这是把代码转换到另外一种形式
可以用定义好的宏来再次定义宏:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; while宏:表示条件成立的循环 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-syntax while
(syntax-rules ()
((_ pred b1 ...)
(let loop () (when pred b1 ... (loop))))))
;; (let ((i 0))
;; (while (< i 10)
;; (display i)
;; (display #Space)
;; (set! i (+ i 1))))
;; => 0 1 2 3 4 5 6 7 8 9
;; ;Unspecified return value
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; for宏:表示数字在范围之内的循环 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-syntax for
(syntax-rules ()
((_ (i from to) b1 ...)
(let loop((i from))
(when (< i to)
b1 ...
(loop (1+ i)))))))
;; (for (i 0 10)
;; (display i)
;; (display #Space))
;; => 0 1 2 3 4 5 6 7 8 9
;; ;Unspecified return value
多种模式
syntax-rules 可以支持定义多种模式。incf 宏是增加变量的值,如果不传入增加的值,就默认增加 1, 如果给定,就增加给定的值:
;; incf宏
(define-syntax incf
(syntax-rules ()
((_ x) (begin (set! x (+ x 1)) x)) ; 如果不给增加参数,默认增加1
((_ x i) (begin (set! x (+ x i)) x))))
;; (let ((i 0) (j 0))
;; (incf i)
;; (incf j 3)
;; (display (list 'i '= i))
;; (newline)
;; (display (list 'j '= j)))
;; => (i = 1)
;; (j = 3)
;; ;Unspecified return value
递归定义
syntax-rules 支持递归定义宏:
(define-syntax my-and
(syntax-rules ()
((_) #t)
((_ e) e)
((_ e1 e2 ...)
(if e1
(my-and e2 ...)
#f))))
;; (my-and) ; => #t
;; (my-and #f) ; => #f
;; (my-and (> 2 1)) ; => #t
;; (my-and #t #f) ; => #f
;; (my-and #t (> 2 1)) ; => #t
;; (my-and #t (> 2 1) (< 3 2) (= 1 1))
(define-syntax my-or
(syntax-rules ()
((_) #f)
((_ e) e)
((_ e1 e2 ...)
(let ((t e1))
(if t t (my-or e2 ...))))))
;; (my-or) ; => #f
;; (my-or #t) ; => #t
;; (my-or (< 2 1)) ; => #f
;; (my-or #f #f) ; => #f
;; (my-or #f (> 2 1)) ; => #t
;; (my-or #f (> 2 1) (< 3 2) (= 1 1)) ; => #t
保留关键字
syntax-rules 的第一个参数是一组 保留关键字 的列表,这些关键字在转换的时候不会被替换。下面是自定义的 my-cond 宏, else 就是这个宏的保留关键字:
(define-syntax my-cond
(syntax-rules (else)
((_ (else e1 ...))
(begin e1 ...))
((_ (e1 e2 ...))
(when e1 e2 ...))
((_ (e1 e2 ...) c1 ...)
(if e1
(begin e2 ...)
(cond c1 ...)))))
;; (my-cond (else (+ 1 2))) ; => 3
;; (my-cond ((> 1 0) (+ 1 2))) ; => 3
;; (my-cond ((< 1 0) (+ 1 2))) ; => ;Unspecified return value
;; (my-cond ((< 1 0) (+ 1 2))
;; ((> 1 0) (+ 2 3))) ; => 5
;; (my-cond ((< 1 0) (+ 1 2))
;; (else (+ 2 3))) ; => 5
局部宏
let-syntax 和 letrec-syntax 可以被用来定义函数中的 局部宏
浙公网安备 33010602011771号