Lisp是如何动态编译的?

时间:2012-09-26 02:31:38

标签: lisp common-lisp

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

3 个答案:

答案 0 :(得分:36)

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

Lisp上下文中的

Dynamic 意味着代码在运行时具有一定的灵活性。例如,它可以更改或替换。这与动态类型不同。

在Lisp中编译

Lisp实现通常在运行时提供编译器。当此编译器是 incremental 时,它不需要整个程序,但可以编译单个Lisp表单。然后我们说编译器支持 incremental 编译。

请注意,大多数Lisp编译器不是 Just In Time 编译器。作为程序员,您可以调用编译器,例如在Common Lisp中使用函数COMPILECOMPILE-FILE。然后编译Lisp代码。

此外,大多数具有编译器和解释器的Lisp系统允许自由混合解释和编译代码的执行。

在Common Lisp中,还可以指示编译器编译代码应该是多么动态。像SBCL(或许多其他人)的编译器这样的更高级的Lisp编译器可以生成不同的代码。

示例

(defun foo (a)
  (bar a 3))

以上函数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)

运行时查找

所以你看到对BAR的调用进行了运行时查找。它查看符号 BAR,然后调用符号的函数。因此,符号表用作全局函数的注册表。

这个运行时查找结合增量编译器 - 在运行时可用 - 允许我们生成Lisp代码,编译它,将其加载到当前的Lisp系统中并让它一块一块地修改Lisp程序。

这是通过使用间接完成的。在运行时,Lisp系统会查找名为bar的当前函数。但请注意,这与编译或解释无关。如果您的编译器编译foo并且生成的代码使用此机制,则它是动态。因此,您将在已解释和已编译的代码中获得查找开销。

自70年代以来,Lisp社区投入了大量精力使编译器和解释器的语义尽可能相似。

像Common Lisp这样的语言也允许编译器使编译的代码不那么动态。例如,在运行时没有为代码的某些部分查找函数。

答案 1 :(得分:3)

  

对于能够操作,修改和生成代码的语言,是否需要进行解释?

没有

  

语言是否可以完全编译并且仍然是动态的?

  

或者我错过了什么?

  

Lisp做了什么允许它既编译又动态?

它是动态编译的,就像大多数java和PyPy的实现一样。

答案 2 :(得分:0)

它可以在同一时间编译和动态,因为它是后期绑定的。您可以运行函数和参数列表,然后向其中添加一些内容,然后再次运行它。基本上,代码的每个部分都可以运行,而不仅仅是整个函数。