第 8 讲 何时使用宏.pdfVIP

  1. 1、原创力文档(book118)网站文档一经付费(服务费),不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。。
  2. 2、本站所有内容均由合作方或网友上传,本站不对文档的完整性、权威性及其观点立场正确性做任何保证或承诺!文档内容仅供研究参考,付费前请自行鉴别。如您付费,意味着您自己接受本站规则且自行承担风险,本站不退款、不进行额外附加服务;查看《如何避免下载的几个坑》。如果您已付费下载过本站文档,您可以点击 这里二次下载
  3. 3、如文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“版权申诉”(推荐),也可以打举报电话:400-050-0827(电话支持时间:9:00-18:30)。
  4. 4、该文档为VIP文档,如果想要下载,成为VIP会员后,下载免费。
  5. 5、成为VIP后,下载本文档将扣除1次下载权益。下载后,不支持退款、换文档。如有疑问请联系我们
  6. 6、成为VIP后,您将拥有八大权益,权益包括:VIP文档下载权益、阅读免打扰、文档格式转换、高级专利检索、专属身份标志、高级客服、多端互通、版权登记。
  7. 7、VIP文档为合作方或网友上传,每下载1次, 网站将根据用户上传文档的质量评分、类型等,对文档贡献者给予高额补贴、流量扶持。如果你也想贡献VIP文档。上传文档
查看更多
第 8 章 何时使⽤宏 第 8 章 何时使⽤宏 我们如何知道⼀个给定的函数是否真的应该是函数,⽽不是宏呢?多数时候,会很容 易 清楚在哪种情况下需要⽤到宏,哪种情况不需要。缺省情况下,我们应该⽤函 数,因为如果函数能解决问题,⽽偏要⽤上宏的话,会让程序变得不优雅。我们应当 只有在宏能带来特别的好处时才使⽤它们。 什么情况下,宏能给我们带来优势呢?这就是本章的主题。通常这不是锦上添花,⽽ 是⼀种必须。⼤多数我们⽤宏可以做到的事情,函数都⽆法完成。第 8.1 节列出了只 能⽤宏来实现的⼏种操作符。尽管如此,也有⼀⼩类 (但很有意思的) 情况介于两者 之间,对它们来说,不管把操作符实现成函数还是宏似乎都⾔之有理。对于这种情 况,第 8.2 节给出了关于宏的正反两⽅⾯考量。最后,在充 考察了宏的能⼒后,我 们在第 8.3 节⾥转向⼀个相关问题:⼈们都⽤宏⼲什么? 8.1 当别⽆他法时 优秀设计的⼀个通⽤原则就是:当你发现在程序中的⼏处都出现了相似的代码时,就 应该写⼀个⼦例程,并把那些相似的语句换成对这个⼦例程的调⽤。如果也把这条原 则⽤到 Lisp 程序上,就必须先决定这个 ⼦例程 应该是函数还是宏。 有时,可以很容易确定应当写⼀个宏⽽不是函数,因为只有宏才能满⾜需求。⼀个像 1+ 这样的函数或许既可以写成函数也可以写成宏: (defun 1+ (x) (+ 1 x)) (defmacro 1+ (x) (+ 1 ,x)) 但是来⾃第 7 .3 节的 while ,则只能被定义成宏: (defmacro while (test body body) (do () ((not ,test)) ,@body)) ⽆法⽤函数来重现这个宏的⾏为。while 的定义⾥拼接了⼀个作为 body 传⼊ do 的 主体⾥的表达式,它只有当 test 表达式返回 nil 时才会被求值。没有函数可以做到 这⼀点;是因为在函数调⽤⾥,所有的参数在函数调⽤开始之前就会被求值。 当你需要⽤宏时,你看中了它哪⼀点呢?宏有两点是函数⽆法做到的:宏可以控制 (或阻⽌) 对其参数的求值,并且它可以展开进⼊到主调⽅的上下⽂中。任何需要宏 的应⽤,归根到底都是要⽤上述两个属性中的⾄少⼀个。 宏不对其参数进⾏求值 ,这个⾮正式的说法不太准确。更确切的说法应该是,宏能 控制宏调⽤中参数的求值。取决于参数在宏展开式中的位置,它们可以被求值⼀ 次,多次,或者根本不求值。宏的这种控制主要体现在四个⽅⾯: 1. 变换 Common Lisp 的 setf 宏就是这类宏中的⼀员,它们在求值前都会对传⼊的参数严加检 查。内置的访问函数 (access function ) 通常都有⼀个对应的逆操作,其作⽤是对该 访问函数所获取的对象赋值。car 的逆操作是 rplaca ,对于 cdr 来说是 rplacd ,等等。 有了 setf ,我们就可以把对这些访问函数的调⽤当成变量赋值。(setf (car x) a) 就是个例⼦,这个表达式可以展开成 (progn (rplaca x a) a). 为了有这样的效果,setf 必须⾮常了解它的第⼀个参数。如果要知道上述的情况需要 ⽤到 rplaca , setf 就得清楚它的第⼀个参数是个以 car 开始的表达式。这样的话,setf 以及其他修 改参数的操作符,就必须被写成宏。 2. 绑定 词法变量必须在源代码中直接出现。例如,由于 setq 的第⼀个参数是不求值的,所 以,所有在setq 之上构建的东西都必须是展开到setq 的宏,⽽不能是调⽤它的函数。 对于 let 这样的操作符也是如此,它的实参必须作为 lambda 表达式的形参出现,还有 类似 do 这样展开到 let 的宏也是这样,等等。任何新操作符,只要它修改了参数的词 法绑定,那么它就必须写成宏。 3. 条件求值 函数的所有参数都会被求值。在像 when 这样的结构⾥,我们希望⼀些参数仅在特定 条件下才被求值。只有通过宏才可能获得这种灵活性。 4. 多重求值 函数的所有参数不但都会被求值,⽽且求值的次数都正好是⼀次。我们需要⽤宏来定 义像 do 这样的结构,这样⼦,就可以对特定的参数多次求值。 也有⼏种⽅式可以利⽤宏产⽣的内联展开式带来的优势。这⾥必须强调⼀点,宏展开 后⽣成的展开式将会出现在宏调⽤所在的词法环境之中,因为下列三种⽤法有两种都 基于这个事实。它们是: 5. 利⽤调⽤⽅环境 宏⽣成的展开式可以含有这样的变量,变量的绑

文档评论(0)

daijun + 关注
实名认证
文档贡献者

该用户很懒,什么也没介绍

1亿VIP精品文档

相关文档