- 1、本文档共9页,可阅读全部内容。
- 2、原创力文档(book118)网站文档一经付费(服务费),不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
- 3、本站所有内容均由合作方或网友上传,本站不对文档的完整性、权威性及其观点立场正确性做任何保证或承诺!文档内容仅供研究参考,付费前请自行鉴别。如您付费,意味着您自己接受本站规则且自行承担风险,本站不退款、不进行额外附加服务;查看《如何避免下载的几个坑》。如果您已付费下载过本站文档,您可以点击 这里二次下载。
- 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))
您可能关注的文档
最近下载
- OIE《水生动物疫病诊断手册》传染性造血器官坏死病毒 2018版2.3.4章只用条款4.3.1.2.1, 4.3.1.2.3.pdf VIP
- 国开电大《幼儿园课程论》形考形成性考核作业4答案.doc
- 湖南省湘西土家族苗族自治州2022-2023年高一下学期期末地理试题.docx VIP
- 14招标文件要求的其他技术内容或投标人认为需要补充的资料.doc
- 2023年宁夏中考生物试题卷(含答案解析).docx
- 国开电大营销策划案例分析形考任务5答案.docx VIP
- 国开电大可编程控制器应用实训形考任务6实训报告.pdf VIP
- 高中英语教学设计 Lesson_3_“White_Bikes“_on_the_Road.docx
- 成都石室中学自主招生考试数学试卷.doc VIP
- 变频调速电梯系统设计.docx
文档评论(0)