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 的 第二个参数 是一个两个元素的 列表 :

  1. 第一个元素:转换前的代码,其中 _ 代表宏的名字
  2. 第二个元素:转换后的代码

注意:如果把上面的代码可以写成函数,但是因为闭包的原因,传递进去的参数实际上是不会改变的

(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 可以被用来定义函数中的 局部宏

 

posted on 2021-10-06 17:51  深蓝医生  阅读(555)  评论(1编辑  收藏  举报

导航