管理Common Lisp中宏扩展顺序的规则

时间:2016-12-05 12:12:52

标签: macros lisp common-lisp clisp

http://clhs.lisp.se/Body/m_defmac.htm记录了

defmacro,但文档并未完全清楚何时发生。通过Clisp实验,我发现了以下内容(假设在顶层定义了所有宏和函数):

  • 直接的顶级代码只能调用之前定义的宏和函数。

  • 宏或函数中的代码,或由宏生成的代码,可以调用它喜欢的任何函数,包括稍后定义的函数(正如预期的那样支持相互递归)。

  • 宏中的代码只能调用比第一个宏的调用站点更早定义的宏。

  • 宏生成的代码可以调用稍后定义的宏。

Clisp是否只是遵循规范,或者在这方面的实现之间是否有任何差异?

在任何地方都记录了确切的预定规则及其背后的基本原理吗?

1 个答案:

答案 0 :(得分:2)

您在询问宏扩展 - 但我想澄清一下如何处理函数。

注意调用和定义何时实际发生。在第二点中,您说函数中的代码可以调用稍后定义的函数。这并非严格意义上说。

在像C ++这样的语言中,您声明并定义函数,然后编译您的应用程序。忽略内联,模板,lambdas和其他魔法......,在编译函数时,需要存在该函数使用的所有其他函数的声明 - 并且在链接时,需要存在编译的定义 - 所有这些都在程序之前开始跑步一旦程序开始运行,所有功能都已经完全准备好并准备好被调用。

现在在Lisp中,情况有所不同。暂时忽略编译 - 让我们考虑解释环境。如果您运行:

;; time 1
    (defun a () (b))
;; time 2
    (defun b () 123)
;; time 3
    (a)

在时间1,你的程序没有任何功能。

然后,第一个defun创建一个函数(lambda () (b)),并将其与符号a相关联。此函数包含对符号b的引用,但此时它未调用 b。 <{1}}只会在调用a时调用b

因此,在时间2,您的程序有一个与符号a相关联的函数,但它尚未执行。

现在,第二个a创建了一个函数defun,并将其与符号(lambda () 123)相关联。

在时间3,您的程序有两个功能,与符号ba相关联,但尚未调用。

现在你致电b。在执行期间,它会查找与符号a关联的函数,在此时发现此类函数已存在并调用它。 b执行并返回123。

让我们添加更多代码:     ;;时间4         defun b()456)     ;;时间5         (a)中

在时间4之后,新的b创建一个返回456的函数,并将其与符号defun相关联。这将替换引用函数返回123的引用b,然后将其进行垃圾回收(或者您实现的任何操作都可以删除垃圾)。

调用b(或更正确地说,符号a的函数属性引用的lambda)现在将调用返回456的函数。

相反,如果我们原来写的是:

a

...这会有效,因为在我们调用;; time 1 (defun a () (b)) ;; time 2 (a) ;; time 3 (defun b () 123) 的时间2之后,它无法找到与符号a相关联的函数,因此它会失败。

现在 - bcompile,优化和其他魔法可以做各种与我上面描述的不同的时髦事物,但要确保你先担心这些基础知识然后再担心关于那些更先进的东西。

  1. 仅在调用eval-when时创建函数。 (翻译不会“向前看文件”。)
  2. 符号的一个属性是对函数的引用。 (函数本身实际上没有名称。)
  3. 多个符号可以引用相同的功能。 (defun
  4. 定义调用函数(setf (symbol-function 'd) (symbol-function 'b))的函数a(通俗地说),只要符号b在时间{{1}之前具有关联函数,就可以了被称为。 (b ning a时不需要。)
  5. 符号可以指不同时间的不同功能。这会影响“调用”该符号的任何函数。
  6. 的规则是不同(它们的扩展在“读取”时间之后是静态的),但许多原则保持不变(Lisp没有“在文件中向前看”以查找他们)。了解Lisp程序比您可能习惯的大多数(较小的;-))语言更具动态性和“运行时”。在执行Lisp程序期间理解发生了什么,并且管理宏扩展的规则将开始有意义。