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 )(y ))
    (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\n"))) (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\n" (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\n" (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

scheme 宏macro写法的更多相关文章

  1. flask第30篇——宏macro和import标签

    宏是Jinja2特有的,像Django则没有这个. 先新建一个项目macroDemo: 然后在templates文件夹中新建index.html文件,并在代码中返回渲染后的文件: 然后回到index. ...

  2. Flask基础(15)-->模板代码的复用【宏(Macro)、继承(Block)、包含(include)】

    宏 对宏(macro)的理解: 把它看作 Jinja2 中的一个函数,它会返回一个模板或者 HTML 字符串 为了避免反复地编写同样的模板代码,出现代码冗余,可以把他们写成函数以进行重用 需要在多处重 ...

  3. C语言的宏macro的使用

    C's Macro Introduction 1.The Connect Macros: ## 这是一个预处理连接符,这个操作符主要用来将两个符号连接成为一个完整的宏符号.通过下面的代码,可以看到其具 ...

  4. [易学易懂系列|rustlang语言|零基础|快速入门|(22)|宏Macro]

    [易学易懂系列|rustlang语言|零基础|快速入门|(22)|宏Macro] 实用知识 宏Macro 我们今天来讲讲Rust中强大的宏Macro. Rust的宏macro是实现元编程的强大工具. ...

  5. FreeMarker学习(宏<#macro>的使用)

    原文链接:https://my.oschina.net/weiweiblog/blog/506301?p=1 用户定义指令-使用@符合来调用  有两种不同的类型:Macro(宏)和transform( ...

  6. word宏(macro) 之 注意事项,常见语法和学习地方

    宏:计算机科学里的宏(Macro),是一种批量处理的称谓.一般说来,宏是一种规则或模式,或称语法替换 ,用于说明某一特定输入(通常是字符串)如何根据预定义的规则转换成对应的输出(通常也是字符串).这种 ...

  7. zabbix上的宏(macro)介绍

    宏:macro,预设的文本替换模式: 宏是一种抽象概念(Abstraction),它根据一些列预定义的规则替换一定的文本模式,而解释或编译器在遇到宏时会自动进行这一模式替换.类似地,zabbix基于宏 ...

  8. C/C++ 中的宏/Macro

    宏(Macro)本质上就是代码片段,通过别名来使用.在编译前的预处理中,宏会被替换为真实所指代的代码片段,即下图中 Preprocessor 处理的部分. C/C++ 代码编译过程 - 图片来自 nt ...

  9. 关于宏MACRO,我们需要知道的事

    一.先从最宏观的角度来了解宏,这里的宏观角度是指程序的运行流程: 1,提交代码后,SAS先把代码读取储存到堆栈中: 2,用文本扫描插件来扫描堆栈中的代码,从上到下,从左到右: 3,扫描到一个分号,则编 ...

随机推荐

  1. Android 中的接口回调

    http://blog.csdn.net/wangjinyu501/article/details/22052187   在Android中到处可见接口回调机制,尤其是UI事件处理方面.举一个最常见的 ...

  2. ulimit -c unlimited

    tomcat 产生core日志: app:/usr/local/apache-tomcat-7.0.55_8082/logs# ulimit -a core file size (blocks, -c ...

  3. linux svn用法

    创建一个版本库.项目目录. 创建一个版本库: svnadmin create ~/SVNTestRepo 创建一个项目目录: svn mkdir file:///home/lsf/SVNTestRep ...

  4. [置顶] 正则表达式应用:匹配IP地址

    都知道iP地址有四个数值,三个点号组成.三个数值的具体范围为0到255,为了使用正则表达式匹配就必须分析IP地址的组成 1先分析数值,2再组合数值和点号 1先分析数值 IP地址的数字范围从0到255, ...

  5. Android学习笔记--服务(Service)

    1.服务概述 1.服务是Android四大组件之一,在使用上可以分为本地服务和远程服务,本地服务是指在不影响用户操作的情况下在后台默默的执行一个耗时操作,例如下载,音频播放等.远程服务是指可以供其他应 ...

  6. html5 音频

    目前,web页面上没有标准的方式来播放音频文件,大多数的音频文件是使用插件来播放,而众多的浏览器使用了不同的插件.而html5的到来,给我们提供了一个标准的方式来播放web中音频文件,用户不再为浏览器 ...

  7. Android中Binder的基础知识点

    Android Binder基础知识点 一 传统IPC和Binder机制的比较 传统IPC: 1)收方无法获得对方进程可靠的UID/PID,从而无法鉴别对方身份. 2)接入点开放,无法建立私有通道. ...

  8. SSH连接LINUX乱码解决方法

    1.vi /etc/sysconfig/i18n 将内容改为 LANG="zh_CN.GB18030" LANGUAGE="zh_CN.GB18030:zh_CN.GB2 ...

  9. hitTest:withEvent:方法(此方法可实现点击穿透、点击下层视图功能)

    此方法可实现点击穿透.点击下层视图功能 一. hitTest:withEvent:调用过程 iOS系统检测到手指触摸(Touch)操作时会将其放入当前活动Application的事件队列,UIAppl ...

  10. C#参数传递、引用类型、值类型等的理解

    本博客不属于技术贴,主要是记录一些自己对不懂得地方的理解和学习的记录,请带着批判的眼光阅读~ 值类型存储在栈上,引用类型存储在堆上.栈是由高到低存储的,遵循先进后出的原则,是内存提前分配好的区域,内存 ...