Optimization 是否有可能/有哪些示例使用卫生宏进行编译时计算优化?

Optimization 是否有可能/有哪些示例使用卫生宏进行编译时计算优化?,optimization,macros,scheme,lisp,Optimization,Macros,Scheme,Lisp,我一直在通读,它说(关于clojure的宏) 另一个例子是在编译时执行昂贵的计算作为优化 我抬头一看,clojure似乎有不卫生的宏。这是否也适用于卫生产品?特别是关于计划。据我所知,宏只转换语法,但不管发生什么,代码的实际执行都会推迟到运行时 是的。只是指宏扩展是否会意外捕获标识符。无论宏是否卫生,常规宏扩展(与读取器宏扩展相反)都会在编译时发生。宏展开将宏的代码替换为执行宏的结果。它们的两个主要用例是转换语法(即DSL),通过在运行时消除计算或同时消除计算来提高性能 我想到了几个例子: 您更

我一直在通读,它说(关于clojure的宏)

另一个例子是在编译时执行昂贵的计算作为优化

我抬头一看,clojure似乎有不卫生的宏。这是否也适用于卫生产品?特别是关于计划。据我所知,宏只转换语法,但不管发生什么,代码的实际执行都会推迟到运行时

是的。只是指宏扩展是否会意外捕获标识符。无论宏是否卫生,常规宏扩展(与读取器宏扩展相反)都会在编译时发生。宏展开将宏的代码替换为执行宏的结果。它们的两个主要用例是转换语法(即DSL),通过在运行时消除计算或同时消除计算来提高性能

我想到了几个例子:

  • 您更喜欢用角度来编写代码,但所有的计算实际上都是以弧度为单位的。您可以让宏在编译时消除这些琐碎但不必要的(在运行时)转换
  • 是宏可用于的计算优化的一个广泛示例
  • 您有一个表示SQL语句或复杂文本数学表达式的字符串,您希望在编译时对其进行解析甚至执行
    您还可以结合这些示例,使用一个记忆化SQL解析器。几乎所有在编译时拥有所有必要输入并因此可以计算结果的场景都是候选场景。

    是的,宏可以做这类事情。这里的一个例子是Racket中一个名为
    plus
    的宏,它类似于
    +
    ,只是在宏扩展时,它对相邻文字数字的序列求和。因此,它完成了您可能期望在宏扩展时的运行时完成的一些工作(因此,实际上是在编译时)。比如说

    (plus a b 1 2 3 c 4 5)
    
    扩展到

    (+ a b 6 c 9)
    
    关于这个宏的一些注释

    • 这可能不是一个很地道的骗局,因为我是一个未经改造的CL黑客,这意味着我住在一个山洞里,穿着兽皮,经常说“ug”。特别是,我确信我应该使用
      语法解析
      ,但我不能理解它
    • 这甚至可能是不对的
    • 算术有一些微妙之处,这意味着这个宏可以返回与
      +
      不同的结果。特别是
      +
      定义为从左到右成对添加,而
      plus
      通常不这样做:所有的文字都是先添加的(假设您已经添加了(要求racket/flonum和
      +max.0
      &c的值与我的机器上的值相同),然后
      (+-max.0 1.7976931348623157e+308 1.7976931348623157e+308)
      的值为
      1.7976931348623157e+308
      ,而
      (加上-max.0 1.7976931348623157e+308 1.7976931348623157e+308)
      的值为
      +inf.0
      ,因为这两个文本先添加,然后溢出
    • 一般来说,这是一件无用的事情:我认为,可以安全地假设,任何合理的编译器都会为您进行此类优化。它的唯一目的是显示可以检测并编译掉编译时常量
    • 值得注意的是,至少从像我这样的穴居人lisp用户的角度来看,您可以像对待
      +
      一样对待它,因为
      语法案例中的最后一个例子是:
      (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]))
    

    事实上,可以更积极地这样做,从而将
    (加上一个b12c3)
    变成
    (+abc6)
    。这可能更令人兴奋,可能会得到不同的答案。值得注意的是,CL规范:

    对于数学上具有关联性(可能是可交换的)的函数,一致性实现可以以与关联性(可能是可交换的)重排一致的任何方式处理参数。这不影响参数形式的计算顺序[…]。未指定的只是处理参数值的顺序。这意味着应用自动强制的实现可能不同[…]

    所以像这样的优化在CL中显然是合法的:我不清楚它在Racket中是否合法(尽管我认为它应该是合法的)


    如果宏没有执行,它们是如何转换语法的呢?一个简单的例子:
    (定义语法编译时计算(λ(stx)(使用语法((([exp)stx)))、“(eval syntax”exp“))
    ,然后当您编写
    (编译时计算(+12))
    时,它会将语法转换为
    (quote 3)
    @PetSerAl不知道语法是
    ;看看它,r6rs支持它,但r7rs不支持它。但我认为它(或者在具体实现中是等效的)编译时计算需要,对吗?我没有做类似的
    语法规则
    语法
    只需要解包语法对象(
    stx
    )和提取第二个列表元素(
    exp
    )如果您能够将任意
    λ
    表单传递给
    定义语法
    ,那么该表单可以表示任意复杂的计算,这将在编译时发生。请注意
    (+2)和
    4`只是不同的语法,它们的执行被推迟到运行时。我们可以在编译时将一种语法转换为另一种语法。我不认为用于记忆的宏是一种优化,因为(据我所知)它也可以用一个常规函数来实现;宏只是让它更易于实现use@CoderinoJavarino宏生成的备忘录实际上可以与
    (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]))