注释:
由于一个简单的原因,回答你的问题几乎不可能:没有几种方法,而是更像一个连续体。在这个连续体中涉及到的实际代码也相当相似,唯一的区别就是事情发生的时间以及是否以某种方式保存了中间步骤。 这个连续体上的各个点(它不是一条线、一个进展,而是更像是一个矩形,有不同的角落可以靠近)包括:
例如,一个完全的解释型编程语言基本上不会进行#4操作,在1和3之间的#2隐含地发生,所以你几乎不会注意它。它只是读取代码的某些部分,并立即对其做出反应。这意味着实际开始执行的开销很小,但是例如在循环中,同样的文本行会被反复读取。
在矩形的另一个角落,传统上有一些编译型语言,其中通常#4项包括将实际机器代码永久保存到文件中,然后以后可以运行。这意味着你要等比较长的时间,在开始时整个程序都被翻译(即使你只调用其中的一个函数),但是循环速度更快,因为不需要再次读取源代码。
然后,还有一些中间状态的东西,例如 虚拟机:为了便携性,许多编程语言不会编译成实际的机器代码,而是编译成字节码。然后有一个编译器生成字节码,一个解释器接受这个字节码并实际运行它(有效地“将其转换成机器代码”)。虽然这通常比直接编译并转移到机器代码要慢,但是将这样的语言移植到另一个平台更容易,因为您只需要移植字节码解释器,这通常是用高级语言编写的,这意味着您可以使用现有的编译器来做到这一点“有效的翻译成机器代码”,而不必为您想要运行的每个平台制作和维护后端。此外,如果您可以将编译到字节码的编译只执行一次,然后只分发编译好的字节码,则可能会更快,这样其他人就不必在例如运行优化程序的CPU周期上花费时间,而只需支付从字节码到本机代码的转换,这在您的使用情况下可能是微不足道的。此外,您不会公开源代码。
另一个中间状态的东西是 即时编译器(JIT),它实际上是一个保留已经运行过的代码的解释器,以编译形式保存。这种“保留”使其比纯解释器更慢(例如增加了开销和内存使用量,导致交换和磁盘访问),但在反复执行一段代码时会更快。如果只重复调用单个函数的代码,则它也可能比纯编译器更快,因为它不会浪费时间编译未使用的程序部分。
最后,您可以找到此矩形上的其他位置,例如不永久保存已编译的代码,而是再次从缓存中清除已编译的代码。这样,您可以节省嵌入式系统上的磁盘空间或RAM,代价可能是必须第二次编译很少使用的代码。许多JIT编译器都这样做。
class IfStatement : AstNode {
private readonly AstNode condition, truePart, falsePart;
public IfStatement(AstNode condition, AstNode truePart, AstNode falsePart) {
this.condition = condition;
this.truePart = truePart;
this.falsePart = falsePart;
}
public override Value Evaluate(EvaluationContext context) {
bool yes = condition.Evaluate(context).IsTrue();
if (yes)
truePart.Evaluate(context);
else
falsePart.Evaluate(context);
return Value.None; // `if` statements have no value.
}
}
这是一个非常简单但完全功能的解释器。