原论文链接失效,特在这里保存一份

http://www.apl.jhu.edu/~hall/Lisp-Notes/Macros.html

Lisp functions take Lisp values as input and return Lisp values. They are executed at run-time. Lisp macros take Lisp code as input, and return Lisp code. They are executed at compiler pre-processor time, just like in C. The resultant code gets executed at run-time. Almost all the errors that result from using macros can be traced to a misunderstanding of this fact.

1. Basic Idea: Macros take unevaluated Lisp code and return a Lisp form. This form should be code that calculates the proper value. Example:

(defmacro Square (X) 
  '(* ,X ,X))

This means that wherever the pre-processor sees (Square XXX) to replaces it with (* XXX XXX). The resultant code is what the compiler sees.

2. Debugging technique: macroexpand-1

When designing a function, you can type a call to the function into the Lisp Listener (prompt), and see if it returns the correct value. However, when you type a macro "call" into the Lisp Listener, two things happen: first, the macro is expanded into its resultant code, and then that code is evaluated. It is more useful during debugging to be able to examine the results of these two steps individually. The function macroexpand-1 returns the result of stage one of this process:

(macroexpand-1 '(Square 9)) ==> (* 9 9)

"If in doubt, macroexpand-1 it out."

3. Purpose: To control evaluation of the arguments.

Since macros are so much harder to use than functions, a good rule of thumb is: don't use defmacro if defun will work fine. So, for example, there would be no reason to try to use a macro for Square: a function would be much easier to write and test. In Lisp, unlike in C, there is no need to use macros to avoid the very small runtime overhead of a function call: there is a separate method for that (the "inline" proclamation) that lets you do this without switching to a different syntax. What macros can do that functions cannot is to control when the arguments get evaluated. Functions evaluate all of their arguments before entering the body of the function. Macros don't evaluate any of their arguments at preprocessor time unless you tell it to, so it can expand into code that might not evaluate all of the arguments. For example, suppose that cond was in the language, but if wasn't, and you wanted to write a version of if using cond.

(defun Iff-Wrong (Test Then &optional Else)(cond(Test Then)(t    Else)))

The problem with this is that it always evaluates all of its arguments, while the semantics of if dictate that exactly one of the Then and Else arguments gets evaluated. For example:

(let ((Test 'A))(Iff-Wrong (numberp Test)(sqrt Test)"Sorry, SQRT only defined for numbers"))

will crash, since it tries to take (sqrt 'A).A correct version, with behavior identical to the built-in if (except that the real if only has one required arg, not two), would be:

(defmacro Iff (Test Then &optional Else)"A replacement for IF, takes 2 or 3 arguments. If the first evaluates tonon-NIL, evaluate and return the second. Otherwise evaluate and returnthe third (which defaults to NIL)"'(cond(,Test ,Then)(t     ,Else)) )

A similar example would be NAND ("Not AND"), which returns true if at least one of the arguments is false, but, like the built-in and, does "short-circuit evaluation" whereby once it has the answer it returns immediately without evaluating later arguments.

(defmacro Nand (&rest Args)'(not (and ,@Args)))

4. Bugs:

  • (A) Trying to evaluate arguments at run-time
  • (B) Evaluating arguments too many times
  • (C) Variable name clashes.
    • (A) Trying to evaluate arguments at run-time

Macros are expanded at compiler pre-processor time. Thus, the values of the arguments are generally not available, and code that tries to make use of them will not work. I.e. consider the following definition of Square, which tries to replace (Square 4) with 16 instead of with (* 4 4).

(defmacro Square (X)(* X X))

This would indeed work for (Square 4), but would crash for (Square X), since X is probably a variable whose value is not known until run-time. Since macros do sometimes make use of variables and functions at expansion time, and to simplify debugging in general, it is strongly recommended that all macro definitions and any variables and functions that they use at expansion time (as opposed to code they actually expand into) be placed in a separate file that is loaded before any files containing code that makes use of the macros.

  • (B) Evaluating arguments too many times

Let's take another look at our first definition of the Square macro.

(defmacro Square (X) '(* ,X ,X))

This looks OK on first blush. However, try macroexpand-1'ing a form, and you notice that it evaluates its arguments twice:

(macroexpand-1 '(Square (Foo 2))) ==> (* (Foo 2) (Foo 2))

Foo gets called twice, but it should only be called once. Not only is this inefficient, but could return the wrong value if Foo does not always return the same value. I.e. consider Next-Integer, which returns 1 the first time called, then 2, then 3. (Square (Next-Integer)) would return N*(N+1), not N2, plus would advance N by 2. Similarly, (Square (random 10)) would not necessarily generate a perfect square! With Lisp you have the full power of the language available at preprocessor time (unlike in C), so you can use ordinary Lisp constructs to solve this problem. In this case, let can be used to store the result in a local variable to prevent multiple evaluation. There is no general solution to this type of problem in C.

(defmacro Square2 (X)'(let ((Temp ,X))(* Temp Temp)))(macroexpand-1 '(Square2 (Foo 2)))
==> (let ((Temp (Foo 2)))(* Temp Temp))

This is what we want.

  • (C) Variable name clashes.

When using let to suppress multiple evaluation, one needs to be sure that there is no conflict between the local variable chosen and any existing variable names. The above version of Square2 is perfectly safe, but consider instead the following macro, which takes two numbers and squares the sum of them:

(defmacro Square-Sum (X Y)'(let* ((First ,X)(Second ,Y)(Sum (+ First Second)))(* Sum Sum)) )

This looks pretty good, even after macroexpansion:

(macroexpand-1 '(Square-Sum 3 4))
==> (LET* ((FIRST 3)(SECOND 4)(SUM (+ FIRST SECOND)))(* SUM SUM))

which gives the proper result. However, this version has a subtle problem. The local variables we chose would conflict with existing local variable names if a variable named First already existed. E.g.

(macroexpand-1 '(Square-Sum 1 First))
==> (LET* ((FIRST 1)(SECOND FIRST)(SUM (+ FIRST SECOND)))(* SUM SUM))

The problem here is that (SECOND FIRST) gets the value of the new local variable FIRST, not the one you passed in. Thus

(let ((First 9)) (Square-Sum 1 First)) 

returns 4, not 100! Solutions to this type of problem are quite complicated, and involve using gensym to generate a local variable name that is guaranteed to be unique.

Moral: even seemingly simple macros are hard to get right, so don't use macros unless they really add something. Both Square and Square-Sum are inappropriate uses of macros.

(defmacro Square-Sum2 (X Y)(let ((First (gensym "FIRST-"))(Second (gensym "SECOND-"))(Sum (gensym "SUM-")))'(let* ((,First ,X)(,Second ,Y)(,Sum (+ ,First ,Second)))(* ,Sum ,Sum))
))

Now

(macroexpand-1 '(Square-Sum2 1 First))
==> (LET* ((#:FIRST-590 1)(#:SECOND-591 FIRST)(#:SUM-592 (+ #:FIRST-590 #:SECOND-591)))(* #:SUM-592 #:SUM-592))

This expansion has no dependence on any local variable names in the macro definition itself, and since the generated ones are guaranteed to be unique, is safe from name collisions.

转载于:https://www.cnblogs.com/jiangxinnju/p/5516879.html

Lecture Notes: Macros相关推荐

  1. Scipy Lecture Notes学习笔记(一)Getting started with Python for science 1.2. The Python language

    Scipy Lecture Notes学习笔记(一)Getting started with Python for science 1.2. The Python language 1.2.2. 基本 ...

  2. C++基本要点复习--------coursera程序设计实习(PKU)的lecture notes

    因为一些特性复杂,很多时候也用不到一些特性,所以忘记了,算是随笔,也当作一个临时查找的手册.没有什么顺序,很杂. 1.构造函数通过函数重载的机制可以有多个(不同的构造函数,参数个数,或者参数类型不同. ...

  3. Grounded Theory and Coding Lecture Notes

  4. lecture notes for investment bank internship

  5. Machine Learning Lecture Notes

    http://www.cs.berkeley.edu/~jordan/courses.html http://www.cs.cmu.edu/~awm/ 转载于:https://www.cnblogs. ...

  6. Stephen P. Boyd convex lecture notes

    昨天没去听Stanford大学的 Stephen 教授(convex optimization 教材的作者)的convex的第一课,甚是可惜.好在他昨天主要讲的是basic  concepts 以及f ...

  7. 个人网页、博客、课程--不断更新

    论文和相关代码 :https://paperswithcode.com/ Caiming Xiong http://www.stat.ucla.edu/~caiming/ 论文,代码,博客 肖小粤的啵 ...

  8. Gradient Descent梯度下降(透彻分析)

    ----------首先了解什么是梯度? 官方解释: 梯度的本意是一个向量(矢量),表示某一函数在该点处的方向导数沿着该方向取得最大值,即函数在该点处沿着该方向(此梯度的方向)变化最快,变化率最大(为 ...

  9. 资源 | 机器学习、NLP、Python和Math最好的150余个教程(建议收藏)

    编辑 | MingMing 尽管机器学习的历史可以追溯到1959年,但目前,这个领域正以前所未有的速度发展.最近,我一直在网上寻找关于机器学习和NLP各方面的好资源,为了帮助到和我有相同需求的人,我整 ...

  10. 3D鸟类重建—数据集、模型以及从单视图恢复形状

    点击上方"小白学视觉",选择"星标" 干货第一时间送达 代码.项目.论文地址:在公众号「计算机视觉工坊」,后台回复「3D鸟类重建」,即可直接下载. 概述 动物姿 ...

最新文章

  1. 系统怎么手动打补丁_韩国服务器不稳定怎么办?
  2. 2015年的ResNet潜力都挖掘出来了吗?新研究:不用蒸馏、无额外数据,性能还能涨一波...
  3. 2208: [Jsoi2010]连通数
  4. 归并排序算法 C++实现与时间复杂度(考过)恋上数据结构笔记
  5. TCP/UDP 常用端口列表汇总
  6. Linux7安装系统服务,linux系统安装及初始服务优化-centos7.4
  7. SpringBoot自动化配置之一:SpringBoot内部的一些自动化配置原理
  8. typedef 为类型取别名
  9. 前端、后台和连接前端后台的网络数据传输
  10. 基于VSFTP的本地YUM源及光盘YUM源搭建
  11. 续易crm源码客户资源管理系统crm源码(源代码c#)
  12. 层次化防御保证企业门户网站安全
  13. 20.案例实战:为@Async实现一个自定义线程池
  14. 计算机专业指南考试题6,计算机专业指南考试题.doc
  15. css鼠标滑过变大,css 鼠标移上去会变大(示例代码)
  16. 全国计算机等级考试网络版客户端,Sctest计算机等级考试系统网络版
  17. Makima修正Hermite插值
  18. AlphaGo Zero算法讲解
  19. python卸载库命令_python常用删除库的方法
  20. 计算机组成原理--填空,简答,判断,计算大题

热门文章

  1. 【leetcode】栈(python)
  2. param.requires_grad = False
  3. Pytorch遍历DataLoader时报错BrokenPipeError: [Errno 32] Broken pipe
  4. ipython notebook使用教程
  5. logistic回归__基于Python和Numpy函数库
  6. 使用keras创建自己的图像标题生成器
  7. dubbo service注解用法_dubbo学习(四)配置dubbo 注解方式配置
  8. sqlite 检索的字段结果前加空格_MySQL5.7分词全文检索思路
  9. python转视频格式高清_树莓派环境下使用python将h264格式的视频转为mp4
  10. 国外企业级区块链 联盟链