第 10 章 其他的宏陷阱.pdf

  1. 1、本文档共9页,可阅读全部内容。
  2. 2、原创力文档(book118)网站文档一经付费(服务费),不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
  3. 3、本站所有内容均由合作方或网友上传,本站不对文档的完整性、权威性及其观点立场正确性做任何保证或承诺!文档内容仅供研究参考,付费前请自行鉴别。如您付费,意味着您自己接受本站规则且自行承担风险,本站不退款、不进行额外附加服务;查看《如何避免下载的几个坑》。如果您已付费下载过本站文档,您可以点击 这里二次下载
  4. 4、如文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“版权申诉”(推荐),也可以打举报电话:400-050-0827(电话支持时间:9:00-18:30)。
查看更多
第 10 章 其他的宏陷阱 第 10 章 其他的宏陷阱 编写宏需要格外⼩⼼。函数被隔离在它⾃⼰的词法世界中,但是宏就另当 论了,因 为它要被展开成进调⽤⽅的代码,所以除⾮仔细编写,否则它将会给⽤户带来意料之 外的不便。第 9 章详细说明了变量捕捉,它是这些不速之客中最常见的⼀个。本章将 讨论在编写宏时需要避免的另外四个问题。 10.1 求值的次数 [⽰例代码 10.1] 控制参数求值 正确的版本: (defmacro for ((var start stop) body body) (let ((gstop (gensym))) (do ((,var ,start (1+ ,var)) (,gstop ,stop)) (( ,var ,gstop)) ,@body))) 导致多重求值: (defmacro for ((var start stop) body body) (do ((,var ,start (1+ ,var))) (( ,var ,stop)) ,@body)) 错误的求值顺序: (defmacro for ((var start stop) body body) (let ((gstop (gensym))) (do ((,gstop ,stop) (,var ,start (1+ ,var))) (( ,var ,gstop)) ,@body))) 在上⼀章中出现了⼏种错误的 for版本。[⽰例代码 10 .1] 给出了另外两个,同时还带 有⼀个正确的版本⽅便对⽐。 尽管第⼆个 for并不那么容易发⽣变量捕捉,但是它还是有个 bug 。它将⽣成⼀个展 开式,在这个展开式⾥,作为 stop 传递的 form 在每次迭代时都会被求值。在最理 想的情况下,这只会让宏变得低效,重复做⼀些它本来可以只做⼀次的操作。如 果 stop 有副作⽤,那么宏可能就会出⼈意料地产⽣错误的结果。例如,这个循环将 永不终⽌,因为⽬标在每次迭代时都会倒退: (let ((x 2)) (for (i 1 (incf x)) (princ i))) 123 5678910111213... 在编写类似 for的宏的时候,必须牢记:宏的参数是 form,⽽⾮值。取决于它们出 现在表达式中位置的不同,它们可能会被求值多次。在这种情况下,解决的办法是把 变量绑定到 stop form 的返回值上,并在循环过程中引⽤这个变量。 除⾮是为了迭代⽽有意为之,否则编写宏的时候,应该确保表达式在宏调⽤⾥出现的 次数和表达式求值的次数⼀致。很明显,这个规则对有些情况并不适⽤:倘若参数总 会被求值的话,Common Lisp 的 or 的⽤处就会⼤打折扣 (那就成 Pascal 的 or 了)。但是在这种情况下⽤户知道他们期望的求值次数。对于第⼆个版本 的 forv来说就不是这样了:⽤户没有理由会想要 stop form 被求值⼀次以上,⽽ 且事实上也不应该这样做。⼀个宏要是写成第⼆个版本的 forv那样,⼗有⼋九就是 弄错了。 对基于 setf 的宏来说,⽆意的多重求值尤其难以处理。Common Lisp 提供了⼏个实 ⽤⼯具以便编写这样的宏。具体的问题,以及解决⽅案,将在第 12 章⾥讨论。 10.2 求值的顺序 表达式求值的顺序,虽然不像它们的求值次数那样重要,但有时先后次序也会成为问 题。在 Common Lisp 的函数调⽤中,参数是从左到右求值的: (setq x 10) 10 (+ (setq x 3) x) 6 对于宏来说,最好也这样处理。宏通常应该确保表达式求值的顺序和它们在宏调⽤中 出现的顺序⼀致。 在 [⽰例代码 10 .1] 中,第三个版本的 for同样有个难以觉察的 bug 。参数 stop 将会 在 start 前被求值,尽管它们在宏调⽤中出现的顺序和求值的顺序是相反的: (let ((x 1)) (for (i x (setq x 13)) (princ i))) 13 NIL 这个宏给⼈⼀种莫名其妙的错觉,就好像时间会倒退⼀样。尽管 start form 在代 码⾥⾯出现在先,但 stop form 的求值操作却能影响 start form 的返回值。 正确版本的 for会确保其参数以它们出现的顺序被求值: (let ((x 1))

文档评论(0)

shaofang00 + 关注
实名认证
内容提供者

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

1亿VIP精品文档

相关文档