37

我不明白 Lisp 是如何编译和动态的。对于一种能够操作、修改和生成代码的语言来说,是不是需要被解释?一种语言是否可以完全编译并且仍然是动态的?还是我错过了什么?Lisp 做了什么让它既可以编译又可以动态化?

4

3 回答 3

42

Lisp 是一个广泛的语言和实现家族。

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

在 Lisp 中编译

通常 Lisp 实现有一个在运行时可用的编译器。当这个编译器是增量编译器时,它不需要整个程序,但可以编译单个 Lisp 形式。然后我们说编译器支持增量编译。

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

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

在 Common Lisp 中,编译器也可以被指示编译代码的动态程度。更高级的 Lisp 编译器,如SBCL(或许多其他)的编译器,可以生成不同的代码。

例子

(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 这样的语言还允许编译器降低编译代码的动态性。例如,在运行时不为代码的某些部分查找函数。

于 2012-09-26T06:23:30.613 回答
0

对于一种能够操作、修改和生成代码的语言来说,是不是需要被解释?

不。

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

是的。

还是我错过了什么?

是的。

Lisp 做了什么让它既可以编译又可以动态化?

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

于 2012-09-26T02:37:07.413 回答
0

It can be compiled and dynamic in the same time because it's late-bound. You can run a list of functions and arguments and then add something to it and then run it again. Basically each part of the code can be run not just entire functions.

于 2016-06-28T18:15:04.723 回答