我不明白Lisp是如何编译和动态的.对于能够操作,修改和生成代码的语言,是否需要进行解释?语言是否可以完全编译并且仍然是动态的?或者我错过了什么?Lisp做的是什么允许它既编译又动态?
Rai*_*wig 38
Lisp是一个广泛的语言和实现系列.
在Lisp的上下文中动态意味着代码在运行时具有一定的灵活性.例如,它可以更改或替换.
在Lisp中编译
通常,Lisp实现在运行时可以使用编译器.当这个编译器是增量的时,它不需要整个程序,但可以编译单个Lisp表单.因此,我们调用编译器来支持渐进式编译.
请注意,大多数Lisp编译器不是Just In Time编译器.作为程序员,您可以调用编译器,例如在Common Lisp中使用函数COMPILE和COMPILE-FILE.然后编译Lisp代码.
此外,大多数具有编译器和解释器的Lisp系统允许自由地混合解释和编译代码的执行.
在Common Lisp中,编译器也可以被指示编译代码应该是多么动态.像SBCL(或许多其他编译器)的编译器这样的更高级的Lisp编译器可以生成不同的代码.
例
(defun foo (a)
(bar a 3))
Run Code Online (Sandbox Code Playgroud)
上面的函数foo调用函数bar.
如果我们有一个函数bar并重新定义它,那么我们通常希望在Lisp bar中调用新函数foo.我们不必重新编译foo.
我们来看看GNU CLISP.它编译为字节代码.它不是本机机器代码,但出于我们的目的,它更容易阅读.
CL-USER 1 > (defun foo (a)
(bar a 3))
FOO
CL-USER 2 > (compile 'foo)
FOO
NIL
NIL
[3]> (disassemble #'foo)
Disassembly of function FOO
(CONST 0) = 3
(CONST 1) = BAR
1 required argument
0 optional arguments
No rest parameter
No keyword parameters
4 byte-code instructions:
0 (LOAD&PUSH 1)
1 (CONST&PUSH 0) ; 3
2 (CALL2 1) ; BAR
4 (SKIP&RET 2)
Run Code Online (Sandbox Code Playgroud)
运行时查找
所以你看到调用进行BAR运行时查找.它查看符号 BAR然后调用符号的函数.因此,符号表用作全局函数的注册表.
这个运行时查找与增量编译器(在运行时可用)相结合,允许我们生成Lisp代码,编译它,将其加载到当前的Lisp系统中,并让它一块一块地修改Lisp程序.
这是通过使用间接完成的.在运行时,Lisp系统查找当前名为的函数bar.但请注意,这与编译或解释无关.如果您的编译器编译foo并且生成的代码使用此机制,那么它是动态的.因此,您将在解释和编译的代码中获得查询开销.
自70年代以来,Lisp社区投入了大量精力使编译器和解释器的语义尽可能相似.
像Common Lisp这样的语言也允许编译器使编译的代码不那么动态.例如,在运行时不查找代码的某些部分的函数.