defmacro
,但文档并未完全清楚何时发生。通过Clisp实验,我发现了以下内容(假设在顶层定义了所有宏和函数):
直接的顶级代码只能调用之前定义的宏和函数。
宏或函数中的代码,或由宏生成的代码,可以调用它喜欢的任何函数,包括稍后定义的函数(正如预期的那样支持相互递归)。
宏中的代码只能调用比第一个宏的调用站点更早定义的宏。
宏生成的代码可以调用稍后定义的宏。
Clisp是否只是遵循规范,或者在这方面的实现之间是否有任何差异?
在任何地方都记录了确切的预定规则及其背后的基本原理吗?
答案 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,您的程序有两个功能,与符号b
和a
相关联,但尚未调用。
现在你致电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
相关联的函数,因此它会失败。
现在 - b
,compile
,优化和其他魔法可以做各种与我上面描述的不同的时髦事物,但要确保你先担心这些基础知识然后再担心关于那些更先进的东西。
eval-when
时创建函数。 (翻译不会“向前看文件”。)defun
)(setf (symbol-function 'd) (symbol-function 'b))
的函数a
(通俗地说),只要符号b
在时间{{1}之前具有关联函数,就可以了被称为。 (b
ning a
时不需要。)宏的规则是不同(它们的扩展在“读取”时间之后是静态的),但许多原则保持不变(Lisp没有“在文件中向前看”以查找他们)。了解Lisp程序比您可能习惯的大多数(较小的;-))语言更具动态性和“运行时”。在执行Lisp程序期间理解在时发生了什么,并且管理宏扩展的规则将开始有意义。