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
- 最简单的创建一个宏的方式是使用 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 - 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.
- 在drracket下:
#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。
- 我们 可以在一个宏里写出多个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 ...))])) - 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
- 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的宏在某些情况下,比较难以得到安全,可靠的转换后的代码。
- C的宏允许我们在宏的实现里写入任意字符串。
#define foo "hello
printf(foo world")这个宏连lexical tokens都不是完整的(此时一个完整的lexcial token为"hello world"). 这对阅读代码,编辑器分析程序源码都是很痛苦的事。我们说这种宏:failed to respect the integrity of lexical tokens。
- 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。
- 我们在宏内使用的名字可能和宏使用的环境的名字冲突:
#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.
- 当宏的参数是一个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 |
这种形式将 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 |
在这个宏执行时,它按照以下形式将 my-if
表达式与模板进行匹配(换句话说,将宏调用与宏定义模式进行匹配):
(my-if (> a b) then a else b) |
因此,在转换表达式中,任何出现 condition
的地方就替换为 (> a b)
。(> a b)
是否是一个列表并不重要。它是包含列表中的一个元素,所以它在模式中作为一个单元。产生的 syntax
表达式只是将每个部分重新安排成一个新的表达式。
这种转换发生在执行之前,这个时期称为宏展开时期(macro-expansion time)。在许多基于编译器的 Scheme 实现中,宏展开在编译时发生。这意味着宏只在程序开始时或编译时执行一次,以后不必再次执行。因此,我们的 my-if
语句没有运行时开销 —— 它在运行时转换为一个简单的 if
。
在下一个示例中,我们要执行 swap!
宏。这个简单的宏要交换两个标识符的值。清单 3 给出了使用这个宏的示例。
清单 3. 使用 swap! 宏交换标识符的值
(define a 1) |
这个简单的宏(清单 4)通过引入一个新的临时变量来实现交换:
清单 4. 定义 swap! 宏
;;Define a new macro |
这个宏引入了一个名为 c
的新变量。但是,如果要交换的参数之一正好也名为 c
,那么会怎么样?
syntax-case
解决这个问题的办法是在宏展开时将 c
替换为一个惟一的未使用的变量名。因此,语法转换器会自己负责这个问题。
注意,syntax-case
没有替换 let
。这是因为 let
是一个全局定义的标识符。
用不冲突的名称替换引入的变量名,这种方法称为健康的(hygiene);产生的宏称为健康的宏(hygienic macros)。健康的宏可以安全地在任何地方使用,不必担心与现有的变量名冲突。对于许多元编程任务,这个特性使宏更可预测并容易使用。
更多看原文。
更多:http://www.shido.info/lisp/scheme_syntax_e.html
scheme 宏macro写法的更多相关文章
- flask第30篇——宏macro和import标签
宏是Jinja2特有的,像Django则没有这个. 先新建一个项目macroDemo: 然后在templates文件夹中新建index.html文件,并在代码中返回渲染后的文件: 然后回到index. ...
- Flask基础(15)-->模板代码的复用【宏(Macro)、继承(Block)、包含(include)】
宏 对宏(macro)的理解: 把它看作 Jinja2 中的一个函数,它会返回一个模板或者 HTML 字符串 为了避免反复地编写同样的模板代码,出现代码冗余,可以把他们写成函数以进行重用 需要在多处重 ...
- C语言的宏macro的使用
C's Macro Introduction 1.The Connect Macros: ## 这是一个预处理连接符,这个操作符主要用来将两个符号连接成为一个完整的宏符号.通过下面的代码,可以看到其具 ...
- [易学易懂系列|rustlang语言|零基础|快速入门|(22)|宏Macro]
[易学易懂系列|rustlang语言|零基础|快速入门|(22)|宏Macro] 实用知识 宏Macro 我们今天来讲讲Rust中强大的宏Macro. Rust的宏macro是实现元编程的强大工具. ...
- FreeMarker学习(宏<#macro>的使用)
原文链接:https://my.oschina.net/weiweiblog/blog/506301?p=1 用户定义指令-使用@符合来调用 有两种不同的类型:Macro(宏)和transform( ...
- word宏(macro) 之 注意事项,常见语法和学习地方
宏:计算机科学里的宏(Macro),是一种批量处理的称谓.一般说来,宏是一种规则或模式,或称语法替换 ,用于说明某一特定输入(通常是字符串)如何根据预定义的规则转换成对应的输出(通常也是字符串).这种 ...
- zabbix上的宏(macro)介绍
宏:macro,预设的文本替换模式: 宏是一种抽象概念(Abstraction),它根据一些列预定义的规则替换一定的文本模式,而解释或编译器在遇到宏时会自动进行这一模式替换.类似地,zabbix基于宏 ...
- C/C++ 中的宏/Macro
宏(Macro)本质上就是代码片段,通过别名来使用.在编译前的预处理中,宏会被替换为真实所指代的代码片段,即下图中 Preprocessor 处理的部分. C/C++ 代码编译过程 - 图片来自 nt ...
- 关于宏MACRO,我们需要知道的事
一.先从最宏观的角度来了解宏,这里的宏观角度是指程序的运行流程: 1,提交代码后,SAS先把代码读取储存到堆栈中: 2,用文本扫描插件来扫描堆栈中的代码,从上到下,从左到右: 3,扫描到一个分号,则编 ...
随机推荐
- 画图工具Graphviz安装配置
Graphviz (英文:Graph Visualization Software的缩写)是一个由AT&T实验室启动的开源工具包,用于绘制DOT语言脚本描述的图形.它也提供了供其它软件使用的库 ...
- 图片延迟加载scrollLoading.js应用
<ul> <li><a href="http://news.qq.com/" target="_b ...
- mongodb----pymongo的用法
pymongo是python的一个模块,可能通过sudo pip install pymongo来安装. 导入pymongo import pymongo 创建连接 conn = pymongo.Co ...
- 在CDHtmlDialog中处理WindowClosing
要截获window.close(),就得截获CDHtmlDialog的WindowClosing.以下是示例代码: // header DECLARE_EVENTSINK_MAP() void Win ...
- Textarea - 百度富文本编辑器插件UEditor
UEditor各种实例演示 Ueditor 是百度推出的一款开源在线 HTML 编辑器. 主要特点: 轻量级:代码精简,加载迅速. 定制化:全新的分层理念,满足多元化的需求.采用三层架构:1. 核心层 ...
- Shortest Word Distance 解答
Question Given a list of words and two words word1 and word2, return the shortest distance between t ...
- 最小费用最大流模板 poj 2159 模板水题
Going Home Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 15944 Accepted: 8167 Descr ...
- 减少GC开销的5个编码技巧
在这篇文章中,我们来了解一下让代码变得高效的五种技巧,这些技巧可以使我们的垃圾收集器(GC)在分配内存以及释放内存上面,占用更少的CPU时间,减少GC的开销.当内存被回收的时候,GC处理很长时间经常会 ...
- linux指定动态运行库的位置
动态运行库在windows.linux下均广泛使用.windows下通常为dll文件,linux下为so文件.不过,对于部署程序,这两个系统查找依赖的运行库文件时却不一样.对于windows而言,优先 ...
- 在OC和Swift中使用IBDesignable/IBInspectable
iOS8新特性IBDesignable/IBInspectable,可以直接在XIB或者Storyboard中直接,设置UI类的属性.例 如:UIView.layer.borderWidth.bord ...