为什么编程语言重要
在计算技术发展伊始,并没有编程语言这个概念。程序看起来就像这样:
00110001 00000000 00000000
00110001 00000001 00000001
00110011 00000001 00000010
01010001 00001011 00000010
00100010 00000010 00001000
01000011 00000001 00000000
01000001 00000001 00000001
00010000 00000010 00000000
01100010 00000000 00000000
该程序计算数字 1~10 之和,并打印出结果:1+2+...+10=55
。该程序可以运行在一个简单的机器上。在早期计算机上编程时,我们需要在正确的位置设置大量开关阵列,或在纸带上穿孔并将纸带输入计算机中。你可以想象这个过程是多么冗长乏味且易于出错。即便是编写非常简单的程序,也需要有经验的人耗费很大精力才能完成。编写复杂的程序则更是难上加难。
当然了,手动输入这些晦涩难懂的位序列(1 和 0)来编写程序的确能让程序员感到很有成就感,而且能给你的职业带来极大的满足感。
在上面的程序中,每行都包含一条指令。我们可以用中文来描述这些指令:
将数字 0 存储在内存地址中的位置 0。
将数字 1 存储在内存地址的位置 1。
将内存地址的位置 1 中的值存储在内存地址的位置 2。
将内存地址的位置 2 中的值减去数字 11。
如果内存地址的位置 2 中的值是 0,则跳转到指令 9。
将内存地址的位置 1 中的值加到内存地址的位置 0。
将内存地址的位置 1 中的值加上数字 1。
跳转到指令 3。
输出内存地址的位置 0 中的值。
虽说这已经比一大堆位序列要好读了许多,但仍然不清晰。使用名称而不是数字用于指令和存储位置有所帮助:
Set “total” to 0.
Set “count” to 1.
[loop]
Set “compare” to “count”.
Subtract 11 from “compare”.
If “compare” is zero, continue at [end].
Add “count” to “total”.
Add 1 to “count”.
Continue at [loop].
[end]
Output “total”.
现在你能看出该程序是如何工作的吗?前两行代码初始化两个内存位置的值:total
用于保存累加计算结果,而count
则用于记录当前数字。你可能觉得compare
的那行代码看起来有些奇怪。程序想根据count
是否等于 11 来决定是否应该停止运行。因为我们的机器相当原始,所以只能测试一个数字是否为 0,并根据它做出决策。因此程序用名为compare
的内存位置存放count–11
的值,并根据该值是否为 0 决定是否跳转。接下来两行将count
的值累加到结果上,并将count
加 1,直到count
等于11
为止。
下面使用 JavaScript 重新编写了上面的程序:
let total = 0, count = 1;
while (count <= 10) {
total += count;
count += 1;
}
console.log(total);
// → 55
这个版本的程序得到了一些改进。更为重要的是,我们再也不需要指定程序如何来回跳转了,而是由while
结构负责完成这个任务。只要我们给予的条件成立,while
语句就会不停地执行其下方的语句块(包裹在大括号中)。而我们给予的条件是count<=10
,意思是“count
小于等于 10”。我们再也不需要创建临时的值并将其与 0 比较,那样的代码十分烦琐。编程语言的一项职责就是,能够帮助我们处理这些烦琐无趣的逻辑。
在程序的结尾,也就是while
语句结束后,我们使用console.log
操作来输出结果。
最后,我们恰好有range
和sum
这类方便的操作。下面代码中的range
函数用于创建数字集合,sum
函数用于计算数字集合之和:
console.log(sum(range(1, 10)));
// → 55
我们可以从这里了解到,同一个程序的长度可长可短,可读性可高可低。第一个版本的程序晦涩难懂,而最后一个版本的程序则接近于人类语言的表达方式:将 1~10 范围内的数字之和记录下来(我们会在后面的章节中详细介绍如何编写sum
和range
这样的函数)。
优秀的编程语言可以为开发人员提供更高层次的抽象,使用类似于人类语言的方式来与计算机进行交互。它有助于省略细节,提供便捷的积木(比如while
和console.log
),允许你定义自己的积木(比如sum
和range
函数),并使这些积木易于编写。。