假设你有这个伪代码
do_something();
function do_something(){
print "I am saying hello.";
}
为什么某些编程语言需要调用 do_something() 出现在函数声明下方才能运行代码?
假设你有这个伪代码
do_something();
function do_something(){
print "I am saying hello.";
}
为什么某些编程语言需要调用 do_something() 出现在函数声明下方才能运行代码?
编程语言使用符号表来保存源代码中使用的各种类、函数等。一些语言在一次编译中进行编译,因此符号在使用后立即从符号表中提取出来。其他人使用两遍,其中第一遍用于填充表格,然后第二遍用于查找条目。
大多数具有静态类型系统的语言都设计为在使用前需要定义,这意味着在调用之前必须有某种函数声明,以便可以检查调用(例如,函数是否获得了正确的数量和类型论据)。这种设计有助于阅读程序的人和编译器:你看到的一切都已经被定义了。易于阅读和一次性编译器的流行可以解释这种设计规则的流行。
不幸的是,使用前的定义不能很好地与相互递归配合使用,因此语言设计者采用了一种丑陋的黑客手段
您在 C 中的类型级别以“不完整struct
声明”的形式看到相同的现象。
大约在 1990 年,一些语言设计者发现没有抽象语法树的一次性编译器应该成为过去,那个时代的两个非常好的设计——Modula -3和Haskell在使用前就摆脱了定义:在那些在语言中,任何定义的函数或变量在其范围内都是可见的,包括定义之前的程序部分。换句话说,相互递归是类型和函数的默认设置。很好,我说——这些语言没有丑陋和不必要的前向声明。
为什么[使用前有定义]?
1975 年轻松编写一次性编译器。
在使用前没有定义,你必须更加努力地考虑相互递归,尤其是相互递归的类型定义。
有些人认为它使人们更容易阅读代码。