我一直在阅读https://lispcast.com/when-to-use-a-macro,它说明(关于clojure的宏) 另一个例子是在编译时执行昂贵的计算作为优化一世 …
是的,卫生的宏可以做这种事情。这里有一个叫做宏的例子 plus 在Racket中就像 + 除了在宏展开时,它对相邻文字数的序列求和。因此,它可以完成您在宏扩展时在运行时完成的一些工作(因此,有效地,在编译时)。所以,例如
plus
+
(plus a b 1 2 3 c 4 5)
扩展到
(+ a b 6 c 9)
关于这个宏的一些注释。
syntax-parse
+max.0
(+ -max.0 1.7976931348623157e+308 1.7976931348623157e+308)
1.7976931348623157e+308
(plus -max.0 1.7976931348623157e+308 1.7976931348623157e+308)
+inf.0
syntax-case
(apply plus ...)
这里是:
(require (for-syntax racket/list)) (define-syntax (plus stx) (define +/stx (datum->syntax stx +)) (syntax-case stx () [(_) ;; return additive identity #'0] [(_ a) ;; identity with one argument #'a] [(_ 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规范 对此说 :
(plus a b 1 2 c 3)
(+ a b c 6)
对于在数学上是关联的(并且可能是可交换的)函数,符合的实现可以以与关联(和可能可交换的)重新排列一致的任何方式处理参数。这不会影响参数形式的评估顺序[...]。未指定的仅是处理参数值的顺序。这意味着实施可能会有所不同,其中应用了自动强制[...]。
因此,这样的优化在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 #'0] [(_ a) ;; identity with one argument #'a] [(_ 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) #'0 #`(#,+/stx #,@nonliterals)) (let ([sum/stx (datum->syntax stx (apply + literals))]) (if (null? nonliterals) sum/stx #`(#,+/stx #,@nonliterals #,sum/stx)))))] [_ ;; plus on its own is +, but we want our one. I am not sure this is right +/stx]))