是的,卫生宏可以做这种事情。例如,这里有一个宏
plus
在球拍中
+
除此之外,在宏扩展时,它对相邻的文字数字序列求和。所以它完成了一些您可能期望在运行时宏扩展时完成的工作(所以,实际上,是在编译时)。比如说
(plus a b 1 2 3 c 4 5)
扩展到
(+ a b 6 c 9)
关于这个宏的一些注释。
-
这可能不是很地道的球拍,因为我是一个基本上未经改造的cl黑客,这意味着我住在一个洞穴里,穿着兽皮,经常说“ug”。尤其是我确信我应该使用
syntax-parse
但我不明白。
-
甚至可能都不对。
-
算术有一些微妙之处,这意味着这个宏可以返回不同于
+
。特别地
+
定义为从左到右添加成对,而
加
通常不会:所有的文字都是首先添加的(假设您已经添加了(需要racket/flonum,和
+max.0
&C的值与在我的计算机上的值相同),然后
(+ -max.0 1.7976931348623157e+308 1.7976931348623157e+308)
值为
1.7976931348623157e+308
,同时
(plus -max.0 1.7976931348623157e+308 1.7976931348623157e+308)
值为
+inf.0
,因为这两个字面值先被添加,然后溢出。
-
总的来说,这是一件无用的事情:我认为,可以肯定的是,任何一个合理的编译器都会为您进行这种优化。它的唯一目的是显示可以检测并编译掉编译时常量。
-
值得注意的是,至少从我这样的穴居人lisp用户的角度来看,您可以像对待
+
因为最后一个
syntax-case
:可以这么说
(apply plus ...)
例如(当然,在这种情况下并没有聪明的优化)。
这里是:
(require (for-syntax racket/list))
(define-syntax (plus stx)
(define +/stx (datum->syntax stx +))
(syntax-case stx ()
[(_)
;; return additive identity
[(_ a)
;; identity with one argument
[(_ a ...)
;; the interesting case: there's more than one argument, so walk over them
;; looking for literal numbers. This is probably overcomplicated and
;; unidiomatic
(let* ([syntaxes (syntax->list #'(a ...))]
[reduced (let rloop ([current (first syntaxes)]
[tail (rest syntaxes)]
[accum '()])
(cond
[(null? tail)
(reverse (cons current accum))]
[(and (number? (syntax-e current))
(number? (syntax-e (first tail))))
(rloop (datum->syntax stx
(+ (syntax-e current)
(syntax-e (first tail))))
(rest tail)
accum)]
[else
(rloop (first tail)
(rest tail)
(cons current accum))]))])
(if (= (length reduced) 1)
(first reduced)
;; make sure the operation is our +
#`(#,+/stx #,@reduced)))]
[_
;; plus on its own is +, but we want our one. I am not sure this is right
+/stx]))
事实上,这样做可能更具侵略性,因此
(plus a b 1 2 c 3)
变成
(+ a b c 6)
. 这可能更令人兴奋可能得到不同的答案暗示。值得注意的是cl规范
says about this
:
对于数学上关联(并且可能交换)的函数,一致性实现可以以与关联(并且可能交换)重排一致的任何方式处理参数。这不影响参数形式的求值顺序[…]。未指定的只是处理参数值的顺序。这意味着应用自动强制的实现可能不同[…]。
所以像这样的优化在cl中显然是合法的:我不清楚它在racket中是否合法(尽管我认为它应该合法)。
(require (for-syntax racket/list))
(define-for-syntax (split-literals syntaxes)
;; split a list into literal numbers and the rest
(let sloop ([tail syntaxes]
[accum/lit '()]
[accum/nonlit '()])
(if (null? tail)
(values (reverse accum/lit) (reverse accum/nonlit))
(let ([current (first tail)])
(if (number? (syntax-e current))
(sloop (rest tail)
(cons (syntax-e current) accum/lit)
accum/nonlit)
(sloop (rest tail)
accum/lit
(cons current accum/nonlit)))))))
(define-syntax (plus stx)
(define +/stx (datum->syntax stx +))
(syntax-case stx ()
[(_)
;; return additive identity
[(_ a)
;; identity with one argument
[(_ a ...)
;; the interesting case: there's more than one argument: split the
;; arguments into literals and nonliterals and handle approprately
(let-values ([(literals nonliterals)
(split-literals (syntax->list #'(a ...)))])
(if (null? literals)
(if (null? nonliterals)
(let ([sum/stx (datum->syntax stx (apply + literals))])
(if (null? nonliterals)
sum/stx
[_
;; plus on its own is +, but we want our one. I am not sure this is right
+/stx]))