2

Scheme宏基础入门(转载)

 2 years ago
source link: https://www.cnblogs.com/bluedoctor/p/15371842.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK