老实说,我都有些怀疑这一章节的必要性。如今的程序员如果在大街上随手扔出一个键盘,砸中的很有可能就是同行。不光日新月异的IT行业吸纳着越来越多的从业人员,编程作为一项专业技能也大有进入中小学生基础知识体系的趋势。但是我想,仍有更多的朋友对编程一知半解或知之甚少,用外行也能理解的方式来捋一捋这门庞杂学问的大致脉络,还是有意义的。
什么是编程
正经地讲,计算机编程(computer programming)是为完成某项特定的计算任务而设计、构建计算机程序的过程。通俗地讲,编程就是编制程序,就是编写、制定一套按顺序执行的工作步骤。
比如老板让你每周五下班前汇总本周的销售数据,在Excel中整理好后与上周情况进行对比分析,群发给各部门的经理,将他们的反馈收齐之后形成一篇周报,下周一上班发给老板。加入这家公司前,你已经具备了数据整理、邮件收发、沟通交流、总结撰写等基础能力,但只有在老板将你的这些能力按特定的顺序和要求进行串联之后,你才能完成本公司所需要的周报。老板在对你进行编程。
计算机本质上是没有自主意识的机器,同样只具备一些通用的基本技能,把这些技能串联起来,让它做出你心目中的「周报」,这就是编程。
编程简史
机器(不光是计算机)帮助人类做事有着很长的历史。早先的机器都是专用的,从建成到报废只能完成某项特定任务,比如张衡的地动仪,除了监测地震之外什么也做不了。后来人们渐渐意识到「通用」的重要性——通过简单的调整或改动能让机器完成多种不同的任务,岂不美哉——结果,可编程(programmable)思想的萌芽在一些与计算毫不相关的领域率先诞生了。
1206年,一位来自阿拉伯的博学家加扎利(Ismail al-Jazari)留下了一本详载着100种机械装置的著作,其中有个类似音乐盒的装置,是一条小船上坐着四个精致的人偶,分别为两名鼓手、一名竖琴师和一名笛手,可以自动演奏,神奇的是还能通过改变插销和凸轮的位置产生不同的旋律与节奏。有学者认为,这是目前已知最早的可编程机器。
然而,这台古老的音乐盒更像是编程史上一个旁枝末节的小插曲,正统的历史要从我国的提花机讲起。
提花机是古代制造丝锦的一种织机,最迟在殷商时期就已出现,后经丝绸之路传入阿拉伯国家,再传到意大利和法国。以其中功能最强的大花楼提花机为例,长约一丈六尺,高约一丈五尺,高起的部分就叫花楼,织锦过程需要上下两人配合完成。
丝锦由丝线纵横交织而成,横的叫纬线,竖的叫经线。经线绷紧之后,由坐在楼下的织花工逐行穿梭纬线。为了织出花纹,每穿一根纬线之前,坐在楼上的提花工都需要提起部分经线,而且每次都不是同一些。
然而,再怎么有经验的提花工都不可能记住每次该提哪些经线,聪明的古人就预先用粗线编制一个与预期图案一样的松松垮垮的网兜,叫做花本,将花本上的线头与经线相连,提花工每次提拉花本上的线就行了。
这种按预定顺序依次提沉经线完成丝锦织造的过程,本质就是编程。
十九世纪初,法国人贾卡(Joseph Marie Jacquard)使用穿孔卡片代替花本,让勾针试探卡片,有孔的地方勾针便穿过卡片,勾起经线。若干年后,英国的发明家巴贝奇(Charles Babbage)利用穿孔卡片构想了一台史无前例的通用型机械计算机——分析机。尽管最终没能建成,当时著名诗人拜伦的女儿艾达却敏锐地指出可以靠它完成很多事情,甚至处理音乐。随着脑海中的分析机嗒嗒运转,她凭空写下了史上第一个计算机程序,其人则被公认为史上第一位程序员。1979年,美国国防部将一种新推出的编程语言命名为Ada,以纪念这位思想远超时代的伟大女性。
有趣的是,从艾达开始,大量杰出的女性为如今这一男性占绝大多数的编程领域做出了一系列重大贡献。
1950年,来自英格兰的凯思琳·布斯(Kathleen Booth)创造了第一套汇编语言;1952年,来自美国的格蕾斯·霍普(Grace Hopper)完成了第一个编译器,她还是COBOL语言之母;ENIAC六位程序员之一的贝蒂·霍伯顿(Betty Holberton)发明了程序调试断点(breakpoint);米莉·科斯(Betty Holberton)的工作成果成为报告生成器的前身;1955年,来自俄罗斯的卡特琳娜·尤先科(Kateryna Yushchenko)所开发的APL语言(Address programming language)对后来的抽象数据类型、面向对象编程、函数式编程、数据库、人工智能等领域影响深远;1961年,来自美国的琼·E·萨米特(Jean E. Sammet)开发了第一个广泛用于形式代数处理的编程语言FORMAC;1965年,玛丽·肯尼斯·凯勒(Mary Kenneth Keller)在威斯康星大学麦迪逊分校获得Ph.D.,成为美国第一个获得计算机博士学位的女性,她在此前参与了BASIC语言的开发;到了70年代,同样来自美国的阿黛尔·哥德堡(Adele Goldberg)成为早期面向对象语言Smalltalk的主要开发者之一;1985年,美国的网络工程师拉迪亚·珀尔曼(Radia Perlman)为交换机设计了著名的生成树协议(STP)……
语言的发展历程
众所周知,计算机只认识0和1两个数字,为了让它认识更多数字,我们就得为每个数字进行编码,比如用0000
表示0、用0001
表示1、用0010
表示2,依次类推,1111
就可以表示15,这就是最基本的二进制编码规律,如果你觉得15太小,我们还可以用更多的位数,比如8位的1111 1111
就可以表示255,16位的1111 1111 1111 1111
就可以表示65535,常用的32位则足以表示4294697295(接近43亿)。
有了数字,我们还需要对运算等操作进行编码,比如用0000
表示加法、用0001
表示减法。对某个数进行加减就需要写成类似0000 0001(加上1)
、0001 0010(减去2)
这样的二进制代码。这种计算机可以直接辨认的代码称为机器码(机器语言)。
久而久之,人们需要计算机处理的问题越来越复杂,对直接编写机器码的做法越来越头大,就想到用人能直接看懂的单词来代替它们。比如用ADD
表示加法,并给寄存器起了AX
、BX
这样的名字,两个寄存相加就可以写成ADD AX, BX
。
这就是我们常听说的汇编语言,它是对机器码的「直译」,翻译工作由汇编器完成。
比起机器码,汇编虽然有了不错的可读性,但仍要求程序员对硬件了如指掌,同一段程序只在一种机器上管用,在另一种用了不同机器码的机器上就不好使了,用现在的话讲叫「可移植性差」或「不具有跨平台性」。这种方式依然不够高级,于是机器码和汇编都被贴上了「低级语言」的标签。
紧接着,更符合人的阅读逻辑、更远离硬件的高级语言就出现了,我们可以直接将两个数字相加而不必留意它们存放在哪。如今常用的C/C++、Java、Python等主流语言都属于高级语言,它们是对汇编的「意译」,可以轻而易举地编写各种复杂的逻辑处理。
比如如下判断a是否比b大的C语言代码段,我们不需要知道a和b在哪个寄存器或在内存的何处,也不用知道结果是如何显示出来的。
if (a > b)
printf("a比b大");
else
printf("a不比b大");
我们将高级语言与汇编的映射交给人与计算机的翻译官——编译器。这位了不起的翻译官通晓多种机器的语言,我们终于不再需要针对每种机器都写一份代码了。
程序诞生的过程
如今一个程序的诞生,恰好是编程语言发展的反演。以C/C++为例:
- 我们首先在文本编辑器中写好代码,这份最初的代码也称为源代码,简称源码,存放源码的文件称为源文件;
- 而后用编译器经过一些列复杂的处理将其翻译为汇编代码;
- 接着使用汇编器进一步译成机器码,与源代码相对,这里的机器码也被称为目标代码,其文件则称为目标文件;
- 如果你的程序涉及多个目标文件,或用到了其他来源的目标文件(往往是库文件),还需要链接器将它们串联起来,最终形成可执行程序(Windows系统上最常见的可执行程序就是后缀名为exe的文件)。
这一过程往往被如今强大的集成开发环境(IDE)所隐藏,比如微软的Visual Studio,你不用知道编译器在哪、汇编器在哪、链接器在哪,它们连同其他各种编程工具都已经集成在这款软件之中,你只需要一键即可生成。
然而,在你觉得这样的编程方式已经足够便捷的时候,一些不知足的天才还在构想更便捷的做法——不需要编译,直接运行源代码。这可能吗?完全可能。我们只需要开发一款软件,它可以直接识别源文件中的代码含义,将其分解为一些最基本的步骤,最终代替源码完成这些步骤,不就可以了吗?
如是,脚本编程的概念便诞生了。你写的源码不再是用来生成可执行程序的代码,而是要喂给另一个程序的脚本(script)。而能够解释、运行脚本的程序被称为解释器。传统编程最终运行的是从源码编译得到的程序,而脚本编程最终运行的是解释器本身,不论你写了多少脚本程序,最终运行的都是同一个解释器。
Web浏览器就是最常见的解释器,它集成了HTML、CSS、JavaScript等多种语言的解释能力,最终呈现出丰富多彩的交互式网页。
当然,用户一般不会为了运行你的脚本而特地安装一个解释器,你也可以借助一些工具将解释器和脚本打包在一起,生成一个完整的可执行程序,发布给别人使用。
脚本编程更加远离硬件,脚本语言是一种「更高级」的编程语言,90年代人称超高级语言。当然我们现在很少这么称呼,而是将它们一并视为高级语言。
传统编程即使无需针对不同机器编写不同代码,但仍需要针对不同机器生成不同的程序。而脚本编程则不然,一份脚本可以在所有有解释器的机器上直接运行,同一个网页可以在各种机器的浏览器中直接打开,程序员终于可以彻底抛弃跨平台方面的顾虑了。
而脚本编程最主要的缺陷——运行效率不高——已被疯狂发展的硬件运算速度忽略到微乎其微,对于普通应用软件根本不值一提。
因此近年来,以Python为代表的脚本语言发展得如火如荼,它们功能强大,而且极易上手。如果你想一窥门径,快速做出自己的软件,不妨直接从脚本编程学起;如果你希望最终融会贯通,成为真正的大神,还老老实实从传统编程,从C语言啃起。
业外人士往往对编程有着两个极端的误解,要么觉得会敲代码的人牛到天上,要么在粗通皮毛之后感觉不过如此,仿佛自己也能在半天时间内搭出一个网站来。其实编程的入门非常简单,一个小白经过最基本的学习就能用黑漆漆的命令行唬住外行,但要成为这一领域的大神绝非一日之寒,里头有你一辈子都学不完的知识,其实大部分从业人员都远称不上是「大神」,我们都只是平凡的码农罢了。