Lisp是如何动态编译的?

Mat*_*att 32 lisp common-lisp

我不明白Lisp是如何编译和动态的.对于能够操作,修改和生成代码的语言,是否需要进行解释?语言是否可以完全编译并且仍然是动态的?或者我错过了什么?Lisp做的是什么允许它既编译又动态?

Rai*_*wig 38

Lisp是一个广泛的语言和实现系列.

在Lisp的上下文中动态意味着代码在运行时具有一定的灵活性.例如,它可以更改或替换.

在Lisp中编译

通常,Lisp实现在运行时可以使用编译器.当这个编译器是增量的时,它不需要整个程序,但可以编译单个Lisp表单.因此,我们调用编译器来支持渐进式编译.

请注意,大多数Lisp编译器不是Just In Time编译器.作为程序员,您可以调用编译器,例如在Common Lisp中使用函数COMPILECOMPILE-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这样的语言也允许编译器使编译的代码不那么动态.例如,在运行时不查找代码的某些部分的函数.

  • @Marcin:现在更加啰嗦. (14认同)
  • 这不是说 Common Lisp 是晚期绑定的一种令人难以置信的冗长方式吗? (2认同)