首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >这些宏之间有什么区别?

这些宏之间有什么区别?
EN

Stack Overflow用户
提问于 2018-01-08 18:57:34
回答 1查看 125关注 0票数 3

我有一些关于如何在Scheme中运行宏的问题(特别是在鸡肉方案中),让我们考虑下面的示例:

代码语言:javascript
代码运行次数:0
运行
复制
(define (when-a condition . body)
  (eval `(if ,condition
     (begin ,@body)
     '())))

(define-syntax when-b
  (er-macro-transformer
    (lambda (exp rename compare)
      (let ((condition (cadr exp))
            (body (cddr exp)))
        `(if ,condition (begin ,@body) '())))))

(define-syntax when-c
  (ir-macro-transformer
    (lambda (exp inject compare)
      (let ((condition (cadr exp))
            (body (cddr exp)))
        `(if ,condition (begin ,@body) '())))))

(define-syntax when-d
  (syntax-rules ()
    ((when condition body ...)
     (if condition (begin body ...) '()))))
  1. 我可以认为when-a是一个宏吗?我觉得我不能严格地把它看作是一个宏,因为我没有使用define-syntax,但是我无法说出不喜欢这个实现的任何实际原因。
  2. 我的宏卫生吗?
  3. when-bwhen-c有什么区别吗?由于我没有使用renameinject,所以我认为没有。
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-01-08 20:27:11

我能考虑一下什么时候-一个宏吗?我觉得我不能严格地把它看作一个宏,因为我没有使用定义语法,但是我不能说出任何实际的理由不喜欢这个实现。

这类似于宏,但与真正的宏不完全相同,原因如下:

  • 真正的宏和基于eval的“宏”之间的主要区别是,在调用之前,您的方法将评估它的所有参数--。这是一个非常重要的区别。例如:(if #f (error "oops") '())将计算为'(),但(when-a #f (error "oops"))将引发一个错误。
  • 这不卫生。例如,在此之前,人们可能会做一些类似于(eval '(define if "not a procedure"))的事情,这意味着这一评估将失败;在“展开”的体表达式中的if并不是指定义站点上的if
  • 它不会在编译时被展开。这是使用宏的另一个主要原因;编译器将展开宏,并且在运行时不会执行任何计算来执行扩展。宏本身将完全蒸发。只剩下扩张了。

我的宏卫生吗?

只有when-cwhen-d是这样,因为ir-macro-transformersyntax-rules提供了保证。在when-b中,您必须重命名ifbegin,才能使它们引用宏定义站点上的ifbegin版本。

示例:

代码语言:javascript
代码运行次数:0
运行
复制
(let ((if #f))
  (when-b #t (print "Yeah, ok")))

== expands to ==>

(let ((if1 #f))
  (if1 #t (begin1 (print "Yeah, ok"))))

这将失败,因为两个版本的if (这里有一个额外的1后缀注释)引用的是相同的东西,所以我们将以操作符的位置调用#f

相比之下,

代码语言:javascript
代码运行次数:0
运行
复制
(let ((if #f))
  (when-c #t (print "Yeah, ok")))

== expands to ==>

(let ((if1 #f))
  (if2 #t (begin1 (print "Yeah, ok"))))

它将按预期工作。如果您想重写when-b以保持卫生,请这样做:

代码语言:javascript
代码运行次数:0
运行
复制
(define-syntax when-b
  (er-macro-transformer
    (lambda (exp rename compare)
      (let ((condition (cadr exp))
            (body (cddr exp))
            (%if (rename 'if))
            (%begin (rename 'begin)))
        `(,%if ,condition (,%begin ,@body) '())))))

注意额外的%-prefixed标识符,它引用了ifbegin的原始值,因为它们位于宏的定义位置。

时间-b和时间-c之间有什么区别吗?因为我不使用重命名或注入,所以我认为没有。

的确有。隐式重命名宏之所以被调用,是因为它们隐式地重命名了来自使用站点的所有标识符,以及您在正文中引入的每个新标识符。如果注入任何标识符,则取消此隐式重命名,这使得调用代码从卫生角度上可以捕获这些标识符。

另一方面,调用显式重命名宏是因为您必须显式重命名任何标识符,以防止调用代码捕获它们。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48156478

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档