zoukankan      html  css  js  c++  java
  • 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 可以被用来定义函数中的 局部宏

  • 相关阅读:
    .net 1.1 LinkButton代码
    Copy string.Fromat
    公文处理方案实现之使用模板新建文档并合并正文内容
    给表格的TBody加上滚动条
    公开一个博客下载备份器源码
    Hook Javascript Function
    使用Emit动态调用方法(技术原型2)
    函数也有上下文与call与apply的区别
    使用匿名函数在后台线程中设置窗体控件属性
    调试带参数的PLSql语句
  • 原文地址:https://www.cnblogs.com/bluedoctor/p/15371842.html
Copyright © 2011-2022 走看看