• scheme 宏macro写法


    scheme里的宏不同的实现有不同的写法:

    1.mzscheme的define-macro (mzscheme也就是pltschme,也就是drracket,没有define-macro这个关键字)

    语法:(define-macro   macro-name

              (lambda  macro-args)

                macro-body   ......)

    例如:定义when

    (define-macro   when

         (lambda    (test .   branch)

            `(if    ,test

             (begin    ,@brach))))

    其中“·”重音号引入模版,逗号开始的符号为参数,逗号和@开始的被当为列表。

    2.MIT的define-syntax和syntax-rules

    语法:(define    macro-name

              (syntax-rules   ()

               ( (template)  operation)

                ......)  )

    上面的when的定义:

    (define-syntax     when

        (syntax-rules ()

           ((when  test   expr  ...)  (if   test     (begin  expr  ...)))))

    when的定义非常简洁,主要是这种语法的模版非常直观,其中“...”就可以表示有多个参数。

    r5rs文档里面写的:

    Syntax definitions are valid only at the top level of a <program>. They have the following form:

    (define-syntax <keyword> <transformer spec>)

    <Keyword> is an identifier, and the <transformer spec> should be an instance of syntax-rules. The top-level syntactic environment is extended by binding the <keyword> to the specified transformer.

    There is no define-syntax analogue of internal definitions.

    Although macros may expand into definitions and syntax definitions in any context that permits them, it is an error for a definition or syntax definition to shadow a syntactic keyword whose meaning is needed to determine whether some form in the group of forms that contains the shadowing definition is in fact a definition, or, for internal definitions, is needed to determine the boundary between the group and the expressions that follow the group. For example, the following are errors:

    (define define 3)

    (begin (define begin list))

    (let-syntax
      ((foo (syntax-rules ()
              ((foo (proc args ...) body ...)
               (define proc
                 (lambda (args ...)
                   body ...))))))
      (let ((x 3))
        (foo (plus x y) (+ x y))
        (define foo x)
        (plus foo x)))

    Drracket宏:

    http://docs.racket-lang.org/guide/macros.html

    1. 最简单的创建一个宏的方式是使用 define-syntax-rule :
      (define-syntax-rule pattern template)
      
      ;; 一个具体的例子,交换两个变量的值
      (define-syntax-rule (swap a b)
        (let ([tmp a])
          (set! a b)
          (set! b tmp)))

      pattern中的symbol成为 pattern variable, 在template中所有的pattern variable会被具体的 实际调用时候的语法对象所替代。使用:

      (define x 1)
      (define y 2)
      (swap x y)
      x :2
      y:1

    2. The define-syntax-rule form binds a macro that matches a single pattern. The pattern must always start with an open parenthesis followed by an identifier, which is swap in this case. After the initial identifier, other identifiers are macro pattern variables that can match anything in a use of the macro. Thus, this macro matches the form (swap form1form2) for any form1 and form2.
    3. 在drracket下:
    4. #lang racket
       
       (define-syntax-rule (swap a b)
         (let ((tmp a))
           (set! a b)
           (set! b tmp)))
      (let ((x 1)(y 2))
        (swap x y) (list x y))

    define-syntax 和 syntax-rules :

    define-syntax 和 syntax-rules : define-syntax-rule 只能匹配一个pattern,但是使用define-syntax和syntax-rules,我们 可以在一个宏里写出多个pattern-template。

    1. 我们 可以在一个宏里写出多个pattern-template。
      (define-syntax id
       (syntax-rules (literal-id ...)
         [pattern template]
         ...))
      
       ;;一个具体的例子
       (define-syntax rotate
         (syntax-rules ()
           [(rotate a b) (swap a b)]
           [(rotate a b c) (begin
                             (swap a b)
                             (swap b c))]))
      

      pattern可以支持sequence  , 用来表示一个或者多个syntax object.

      (define-syntax rotate
        (syntax-rules ()
          [(rotate a) (void)]
          [(rotate a b c ...) (begin
                                (swap a b)
                                (rotate b c ...))]))
      
    2. procedure macro: 使用syntax-rules我们不能对pattern variable做更多判断(譬如判断macro的参数是否合法等),不能对 template做更多操作。
      (define-syntax swap
       (lambda (stx)
        (syntax-case stx ()
          [(swap x y)
           (if (and (identifier? #'x)
                    (identifier? #'y))
               #'(let ([tmp x])
                   (set! x y)
                   (set! y tmp))
               (raise-syntax-error #f
                                   "not an identifier"
                                   stx
                                   (if (identifier? #'x)
                                       #'y
                                       #'x)))])))
      

      这里对swap参数做了检查,如果这样调用 (swap 10 b) 将会报错,因为10不是一个identifier

    3. transformer: define-syntax 创建一个 transformer ,且绑定一个名字,这个绑定的名字能在编译的时候 用来展开表达式(expand expression)。
      (define-syntax a
        (lambda (stx)
          #'(printf "zh
      ")))
      
      (a)
      

      当然transformer一般使用syntax-rules定义,syntax-rules返回的是一个procedure:

      (syntax-rules () [(nothing) something])
      #<procedure>
      
      (syntax-case #'(+ 1 2) ()
        [(op n1 n2) #'(- n1 n2)])
      
      '(- 1 2)
      

    3 Hygienic macros

    Hygienic(安全)是对Scheme Macro系统描述用的最多的一个词,一般来说,hygienic macros用来表示 表示宏的展开式里引入的变量不会和宏所使用的环境中的变量名冲突。

    一个比较准确的描述:

    If a macro transformer inserts a binding for an identifier, the new binding will 
    not capture other identifiers of the same name introduced elsewhere.
    

    举例来说:

    (define-syntax-rule (swap a b)
      (let ([tmp a])
       (set! a b)
       (set! b tmp)))
    
    (let ([tmp 100] [b 200])
      (swap tmp b)
      (printf "~a
    " (list tmp b)))
    

    (swap tmp b) 展开后定义了一个局部变量 tmp ,他们会swap所使用的环境中的 tmp 不会有 任何关系,他们不会发生冲突。

    另一个常用来形容scheme macro特点的词汇是 referential transparency ,如果一个宏展开式 中引用了一个free variable(非local variable), 那么这个free variable将和宏定义的环境绑定, 而和宏具体使用环境无关。这有点像lexical scoping。

    (define-syntax-rule (swap a b)
      (let ([tmp a])
       (set! a b)
       (set! b tmp)))
    
    (let ([set! 100] [b 200])
      (swap set! b)
      (printf "~a
    " (list set! b)))
    

    在swap的定义里使用了let, set!这两个free variable,他们绑定的是swap定义处的环境,为global namespace。 在swap使用的环境中,可以看到set!已经被定义成一个number,但是这不会对swap展开式中的set!有任何影响。

    当然现在一般使用 hygienic macro 同时表示上面两个scheme macro的特性。

    4 Syntax Object:

    macro transformer的输入输出为syntax object。一个S-exp对应的syntax object包含了值:(quote S-exp), source-location,lexical-information(用来保证hygienic特性). source-location一般是parse 源代码的时候 加入(看另一篇文章racket reader). 创建一个syntax Object很简单:

    (syntax (+ 1 2))
    #<syntax:1:0 (+ 1 2)>
    

    5 C Macro

    通过 #define 的形式定义的pre-processor,他是C语言的重要组成部分。C的宏能帮我们做很多事情, 譬如定义常量,省些重复代码,内联代码等。

    6 Scheme Macro 与 C Macro的比较

    C宏的优点暂且不说,这里只说下缺点。C的宏在某些情况下,比较难以得到安全,可靠的转换后的代码。

    1. C的宏允许我们在宏的实现里写入任意字符串。
      #define foo "hello
      printf(foo world")
      

      这个宏连lexical tokens都不是完整的(此时一个完整的lexcial token为"hello world"). 这对阅读代码,编辑器分析程序源码都是很痛苦的事。我们说这种宏:failed to respect the integrity of lexical tokens。

    2. C的宏可以展开成任意词法序列:
      #define mul(a, b)  a*b
      add(x-y, x+y)
      
      exand to:
      x-y*x+y
      

      正因为此,我们在初次学习C的宏的时候,就会被告知,宏的实现体里一定要把参数括起来!但即便如此, 我在实际工作中还是出现了忘了括小括号,结果导致了错误。这种现象叫做:failed to respect the structure of expressions。

    3. 我们在宏内使用的名字可能和宏使用的环境的名字冲突:
      #define swap(v, w) { int tmp = (v);
           (v) = (w); (w) = tmp;}
      
      int tmp = 100;
      int atmp = 200;
      swap(tmp, atmp);
      

      所以,我们在学习C宏的时候还是被告知,宏内引入的名字(这里譬如tmp)应该使用一个比较特殊的名字, 譬如_tmp.但是这只能说是一个权宜之计,当swap这个宏在另一个宏内使用,而令一个宏恰巧也使用了 _tmp这个变量,那么还是有可能造成错误。这种现象叫做:fail to respect the correlation between bindings and uses of names.

    4. 当宏的参数是一个expression的时候,可能有side effect
      #define discriminant(a,b,c)  ((b)*(b)-4*(a)*(c))
      
      discriminant(3, x--, 2)
      

      这种问题在C的宏系统里无法避免,只能靠程序员细心去避免。但是在scheme的宏里,我们可以通过定义新的 变量的方式来避免这种问题。

    7 总结:

    Scheme Macro非常强大,安全,成熟,他也成为很多其他语言提供宏机制的参考之一。当然也有缺点,就目前我的认识,我认为最为困难的地方在于难以掌握,但是一旦掌握 了Scheme Macro背后的实现,很多难以理解的地方也就豁然开朗。之后我将在写两篇文章,一是深入Scheme Macro,二是聊聊 Scheme Macro的实现。

    转自:http://blog.csdn.net/cnnzp/article/details/8307798

    ---------------------

    一篇非常好的文章:

    http://www.ibm.com/developerworks/cn/linux/l-metaprog2.html

    用 Scheme 编写 syntax-case 宏

    syntax-case 宏并不是 Scheme 的标准部分,但是它们是使用最广泛的宏类型,允许健康和非健康形式,与标准的 syntax-rules 宏密切相关。

    syntax-case 宏采用清单 1 所示的形式:


    清单 1. syntax-case 宏的一般形式

    (define-syntax macro-name
       (lambda (x)
         (syntax-case x (other keywords go here if any)
           (
             ;;First Pattern
             (macro-name macro-arg1 macro-arg2)
             ;;Expansion of macro (one or multiple forms)
             ;;(syntax is a reserved word)
             (syntax (expansion of macro goes here))
           )
           (
             ;;Second Pattern -- a 1-argument version
             (macro-name macro-arg1)
             ;;Expansion of macro
             (syntax (expansion of macro goes here))
           )
     )))
    

    这种形式将 macro-name 定义为用于进行转换的关键字。用 lambda 定义的函数由宏转换器用来将表达式转换为展开形式。

    syntax-case 以表达式 x 作为第一个参数。第二个参数是关键字列表,这些关键字在语法模式中采用字面意义。模式中使用的其他标识符用作模板变量。然后,syntax-case 接受一系列模式/转换器组合。它依次通过每个组合进行处理,尝试将输入形式与模式进行匹配,如果匹配的话,它就产生相关联的展开形式。

    我们来看一个简单的示例。假设我们想编写一个比 Scheme 提供的版本更详细的 if 语句版本。还假设我们想寻找两个变量中比较大的一个并返回它。代码如下:

    (if (> a b) a b)

    对于非 Scheme 程序员,没有明显的文字可以指出哪个是 “then” 分支,哪个是 “else” 分支。为了帮助他们理解代码,可以创建定制的 if 语句,添加 “then” 和 “else” 关键字。代码如下:

    (my-if (> a b) then a else b)

    清单 2 演示执行此操作的宏:


    清单 2. 定义扩展的 if 语句的宏

    ;;define my-if as a macro
    (define-syntax my-if
      (lambda (x)
        ;;establish that "then" and "else" are keywords
        (syntax-case x (then else)
          (
            ;;pattern to match
            (my-if condition then yes-result else no-result)
            ;;transformer
            (syntax (if condition yes-result no-result))
           )
    )))
    

    在这个宏执行时,它按照以下形式将 my-if 表达式与模板进行匹配(换句话说,将宏调用与宏定义模式进行匹配):

    (my-if  (> a b)  then     a      else    b)
       |       |      |       |       |      |
       |       |      |       |       |      |
       v       v      v       v       v      v
    (my-if condition then yes-result else no-result)
    

    因此,在转换表达式中,任何出现 condition 的地方就替换为 (> a b)(> a b) 是否是一个列表并不重要。它是包含列表中的一个元素,所以它在模式中作为一个单元。产生的 syntax 表达式只是将每个部分重新安排成一个新的表达式。

    这种转换发生在执行之前,这个时期称为宏展开时期(macro-expansion time)。在许多基于编译器的 Scheme 实现中,宏展开在编译时发生。这意味着宏只在程序开始时或编译时执行一次,以后不必再次执行。因此,我们的 my-if 语句没有运行时开销 —— 它在运行时转换为一个简单的 if

    在下一个示例中,我们要执行 swap! 宏。这个简单的宏要交换两个标识符的值。清单 3 给出了使用这个宏的示例。


    清单 3. 使用 swap! 宏交换标识符的值

    (define a 1)
    (define b 2)
    (swap! a b)
    (display "a is now ")(display a)(newline)
    (display "b is now ")(display b)(newline)
    

    这个简单的宏(清单 4)通过引入一个新的临时变量来实现交换:


    清单 4. 定义 swap! 宏

    ;;Define a new macro
    (define-syntax swap!
      (lambda (x)
        ;;we don't have any keywords this time
          (syntax-case x ()
            (
              (swap! a b)
              (syntax
                (let ((c a))
                  (set! a b)
                  (set! b c)))
            )
    )))
    

    这个宏引入了一个名为 c 的新变量。但是,如果要交换的参数之一正好也名为 c,那么会怎么样?

    syntax-case 解决这个问题的办法是在宏展开时将 c 替换为一个惟一的未使用的变量名。因此,语法转换器会自己负责这个问题。

    注意,syntax-case 没有替换 let。这是因为 let 是一个全局定义的标识符。

    不冲突的名称替换引入的变量名,这种方法称为健康的(hygiene);产生的宏称为健康的宏(hygienic macros。健康的宏可以安全地在任何地方使用,不必担心与现有的变量名冲突。对于许多元编程任务,这个特性使宏更可预测并容易使用。

    更多看原文。

    更多:http://www.shido.info/lisp/scheme_syntax_e.html

  • 相关阅读:
    Java基础101 给c:forEach的select下拉框中的值,设置默认值(后台传值,前台默认选中)
    Java进阶知识27 SSH整合(Struts2、Spring、Hibernate)
    错误/异常:org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in ServletContext resource [/WEB-INF/classes/beans_common.xml]...的解决方法
    Java进阶知识26 Spring与Hibernate整合到一起
    Java进阶知识25 Spring的事务管理(事务回滚)
    spring各个版本源码
    sql之left join、right join、inner join的区别
    git命令之git stash 暂存临时代码
    apollo配置相关
    idea快捷键
  • 原文地址:https://www.cnblogs.com/youxin/p/3427323.html
Copyright © 2020-2023  润新知