来源 https://www.cnblogs.com/GodWroteinlisp/archive/2012/03/27/2420426.htmlhtml
scheme目前其实有至少三套宏系统:linux
syntax-case彻底不受限制,扩展能力与传统lisp宏(defmacro)是同样的,但因为它自带模式匹配功能,因此写起来会更方便,至少quasiquote,unquote,unquote-splicing少了不少.程序员
由于syntax-case的强大能力,用它来实现define-syntax 也是很简单滴:web
以上代码证实了scheme的清洁宏系统至少和传统lisp宏系统同样强大.express
那么,能不能用非清洁宏系统来实现清洁宏呢?这篇文章有介绍:Hygiene for the Unhygienic编程
scheme里的宏不一样的实现有不一样的写法安全
来源 https://www.cnblogs.com/youxin/p/3427323.htmlapp
1.mzscheme的define-macro (mzscheme也就是pltschme,也就是drracket,没有define-macro这个关键字)编辑器
语法:(define-macro macro-nameide
(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 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
#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。
(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 ...))]))
(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
(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)
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的特性。
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)>
C宏的优势暂且不说,这里只说下缺点。C的宏在某些状况下,比较难以获得安全,可靠的转换后的代码。
#define foo "hello printf(foo world")
这个宏连lexical tokens都不是完整的(此时一个完整的lexcial token为"hello world"). 这对阅读代码,编辑器分析程序源码都是很痛苦的事。咱们说这种宏:failed to respect the integrity of lexical tokens。
#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.
#define discriminant(a,b,c) ((b)*(b)-4*(a)*(c)) discriminant(3, x--, 2)
这种问题在C的宏系统里没法避免,只能靠程序员细心去避免。可是在scheme的宏里,咱们能够经过定义新的 变量的方式来避免这种问题。
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
syntax-case
宏并非 Scheme 的标准部分,可是它们是使用最普遍的宏类型,容许健康和非健康形式,与标准的 syntax-rules
宏密切相关。
syntax-case
宏采用清单 1 所示的形式:
(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 演示执行此操做的宏:
;;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 给出了使用这个宏的示例。
(define a 1) (define b 2) (swap! a b) (display "a is now ")(display a)(newline) (display "b is now ")(display b)(newline) |
这个简单的宏(清单 4)经过引入一个新的临时变量来实现交换:
;;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
============= End