第一章 实现简单需求
一般学习一门计算机语言,通常的做法就是去买本书,然后照着书上的讲解,写好程序,编译运行,然后观察结果是否正确。其中第一个例子,约定俗成地就是在屏幕上打印一行“Hello world.”,这里我们并不想首先就教会您怎么写出一个可以运行的程序,而是通过问题的引出和解决方法的理解软件是用来做什么的,一般用软件可以解决什么样的问题。
人类知识的出现必然有其原因,有需求才有动力,这是市场经济的规律,也是人类意识的进化规律。自从有了计算机,人类就将很多的工作移交给软件去实现了,现在人们的工作和生活基本离不开计算机。这里暂且不论计算机是怎么出现的,也不论软件的发展过程,我们仅仅讲解一个简单的需求,怎么通过现有的知识去实现。只要你能明白一个简单的需求的实现过程,就可以通过不断的学习和实践去理解或构建一个复杂的系统。
问题的提出“2+3=?”,或许您会说,这个太简单了,不是问题,完全没必要通过计算机去解决,但这里我们正是要通过这个简单的问题讲解计算机怎么实现一个人看似简单的解决方法。只要我们能实现2+3的问题,那么我们就可以用计算机实现比人脑更快计算出123456789 + 23456876的结果。进而计算诸如y=sin(x)、y=e x 这样的函数,也可以更进一步构建更复杂的系统。
问题的解决方法,从来就不会只有一种,无论你在生活中,还是在编写程序时,都不要将问题的解决局限在一个狭小的空间中,当问题出现时,开放自己的思维,找到所有可能的方法,从其中找到一种对于自己来说最有效的方法去解决问题。如果你久经沙场,那么你的直觉就会告诉你什么方法最有效,所以对于不同的解决方案,只有你自己多去试,多亲身体验才能获得解决问题的直觉。例如一个计算机的硬件工程师,首先想到的可能就是通过一个加法器电路来实现2+3=5,而一个软件程序员更可能的解决办法就写个程序来实现2+3=5,而如果需要实现一个计算器的界面,用Delphi实现起来比java就简单得多。各种技术和工具都有其服务的目标,所以必然各有所长,用最合适的技术和工具总能起到事半功倍的效果。
这里我们不讲解怎样找到最合适的方法,因为每个人的知识不一样,只有对自己最合适的,没有对所有人都是最合适的方法。
“如果你有鸡粪,那你就卖鸡粪吧”,我时常在网上看到一些技术之争,如将J2EE与.NET平台进行对比,将Delphi与VC++对比,也有很多刚开始学习的人会问“我应该选择学习哪门语言呢?什么编程语言是目前市场上需要的呢?java程序员工资高还是.net程序员工资高?”,其实无论你选择什么编程技术或工具,你的目标只有一个,那就是写出满足客户需求的软件。如果你是一个初学者,那么你就选择一个你认为最易入门的工具和编程语言。如果是因为工作需要,那么你就选择一个能最快解决工作问题的技术。如果你想成为一个真正的通用型程序员,那么什么都学点。一切没有绝对,如果你目前仅有鸡粪,要取得生存,那么就卖鸡粪吧。所以我们不去争论用哪个技术来解决2+3=5是最好的。
要解决2+3=5这个问题,首先还需要做些前提准备工作。
1.首先,2,3,5这几个符号是什么?
回答“数字”,当然就这么简单,这个问题似乎不是问题。但是为什么你不回答是“数据”,或更抽象化地说“信息”呢。在具体与概念之间总是存在一个联系,那就是抽象,人类的语言就是一种抽象,文字也是一种抽象,而抽象的过程就是一种约定。
现在我们假定,一个现代人与一个原始人相遇了,现代人说“请给我2个苹果”,原始人肯定是不明白的。之所以举这样一个例子,因为计算机只是一个非生物体,比起原始人来说,更无智能可言。要让计算机来解决问题,首要的是要让计算机与人之间达成一致的认同。当我们与另一个实体通过语言交流的时候,我们传达“2”这个信号,必然有一个信号的载体,如通过说的声音,伸出两个指头的肢体语言,或通过书写的文字“2”,或干脆画2个苹果。所有的这些在交流的瞬间,其信号是存在于某个世纪的物体上的。对于受体,如果要明白这个信号,必然预先与发送信号的主体之间达成一致的认同,即每个符号代表什么意义。我们经常在战争电影中看到战士用手语说“3点钟方向有敌人”,这里就事先所有战士都达成了一致认同,用手表的12个点位来表示平面方向,方向指的是以自己还是对方的前面为参考,手语的表示方法等等。这些在计算机领域有一个更加专业化的称呼“协议”,关于协议我们将在后面章节中讲到。
如果将“协议”概念更扩大化,协议=约定,那么可以说一切都是协议。
那现在我们要实现“2”这个数字的表达,那么就需要通过一个载体,从上面知道,人与人之间的沟通,表达“2”有多种方法。但是我们现在是用电路来解决问题,那么,信号自然就用电来表达。而电这个东西是一个看不见的东西,所以我们要知道2+3的计算结果,就需要将电变成其他信号展现出来,人眼才能看到。因为我们并不关心2+3的计算过程,我们仅对结果感兴趣,所以2和3以及“+”法云运算的表达我们并不要求电路能显示给我们看。这里我们暂不讨论怎么显示结果。用电来表示数字“2”,这可以有很多种方法,如通过电压表示,数字1用1V表示,数字2用2V表示,或者,用电流大小表示,1用1毫安表示,2用2毫安表示。如果采用其中任何一种方法,那么是否要表示10000,那就要用10000V或10000mA来表示呢,显然,这种方法是不可行性的。而且如果电路被干扰,那么,数据就会产生错误。
人类进行语言交流的时候,同样的26个字母或几千个汉字,但是可以表达出无穷的含义,其原理是什么,就是符号的组合。那么我们在用电路来表达信号的时候,是否也可以用信号组合的方法来表达更复杂的信号呢。那么我们觉得最简单的,而且不容易出错的方法是什么呢?就是定性而不定量,人类的认知里面,绝大多数是定性认知,而定量要涉及到计算,问题就变得复杂。所以用电的有无来表示信号,变得简单又可靠。
但是怎样将信息用电的有无来表示,最开始也有很多的探索,这个就是信息编码学的范畴,我们在这里并不去具体追究。在计算机领域我们最终采用了二进制来表达信息。
我们知道,电这个物质有两个描述量:电压和电流。
那么是用电压的有无来表示信号好,还是电流表示信号好呢?通常的情况是这样的,由于只要有电流通过,就必然有功率损耗,但是有电压并不一定就有功率损失,因为电路断开的时候灯是熄的就不用耗电,这点好理解。那是不是所有电路的信号表示就只用电压表示呢,也有信号传输过程中就采用电流的有无来表示0和1。
这里提到二进制,就同8进制、10进制、16进制一起了解下。
我们来看数字表示的约定:
10进制 | 2进制 | 16进制 | 8进制 |
2 | 10 | 2 | 2 |
8 | 1000 | 8 | 10 |
16 | 10000 | 10 | 20 |
10 | 1010 | A | 12 |
3 | 11 | 3 | 3 |
5 | 101 | 5 | 5 |
“2”是一个我们通常在语言中使用的10进制数,10进制我们好理解。对于二进制,2=1+1,满2就必须进1,同十进制的满10必须进1一样的道理,所以第2位的1表示的大小是第1位乘上2,即1x2=2,第三位呢,就表示第二位乘上2,即2x2=4, 这就如100=10X10一样。将10换成2就是一样的。所以第8位就是2 x 2 x 2 x 2 x 2 x 2 x2(7个2相乘),我们用指数表示就是27,可以看到,第n位就是2的n-1次方。
我们来看常见的计算机的8位二进制数,就是用8位来表示信号。
全部为1时就是11111111,变成10进制数就是
11111111=27+26+25+24+23+21+20=128+64+32+16+8+4+2+1=255
如果再加1,那么所有位均产生进位,就全部变成0了,在程序中有个概念叫“溢出”。
那么一个8位数能表示多少个不同的信号,是255个么,可不要忘了0也是一个信号哦,因此可以表示256个信号,这正好是28。所以n位二进制数就可以表示2的n次方个不同的符号,由于长度是没有限制的,如果不够用,我们可以用更长的位数来表示不同的信号组合,在人类语言中,我们说话的长短是没有规定的,有时就一个字,有时可能是很长一段话。但是不管怎样,我们的文字基本符号的个数是有限的,而且并不是很多。我们是通过文字的组合来表达一个含义,用英语,也不是用字母的组合来表达一个含义,而是用更高级符号组合即单词(word)来表达一个含义,而汉字也不是用笔划来组合表达一个含义,而是首先用笔划来组合成汉字,然后用汉字来表达一个含义。计算机是对于人类沟通信息领域的一种模拟,所以必然遵从一样的规律。我们并不直接用0和1这两个二进制符号来表达一个复杂的含义,而是首先将0和1组合成一个有意义的基本符号,然后再进行表达。
我们用多少位来表示一个基本符号呢,2位?肯定太少,表示不了几个符号。100位?似乎太多,就如我们不会造10万个汉字去书写一样,太多编码,要在人与人之间沟通反而变得更难。需要花很多时间达成一致的符号理解。
如果仅仅表示0-9这几个10进制数字,需要多少个符号呢?10个,那么需要多少位二进制来表示10个符号呢,答案就是4位可以表示16个不同的符号。
基本符号 | 2进制表示 | 符号组合 | 2进制 |
0 | 0000 | 12 将12看成是一个符号组合
| 是用1100表示么, 不是,否则又回到原来的用0和1表示所有的含义的状态了。 |
1 | 0001 | ||
2 | 0010 | ||
3 | 0011 | 136 注意这里将136看成是一个符号组合,而不是仅仅是一个数字,因为我们要解决更通用性的需求。 | 这里十进制数“136”是由三个更基本的符号组成的“1”、“3”、“6”所以,我们在计算机中也用代表基本符号的二进制组合表示 “0001”“0010”“0110” 1 3 6 这里暂不讨论最终3个符号组合在计算机中怎么存储。 |
4 | 0100 | ||
5 | 0101 | ||
6 | 0110 | ||
7 | 0111 | ||
8 | 1000 | ||
9 | 1001 | ||
| 1010-1111没有用到 | ||
假设我们要用到x、y、z这三个符号,并利用上面4位二进制中余下的组合来表示它们 | |||
x | 1011 | 3z z5y zyz x88yzzz 可以有无穷组合, 每个组合我们可以给他们定义不同的含义。 | 每个组合,我们用4位二进制的组合表示,与上面类似。 |
y | 1100 | ||
z | 1101 |
通过上面数字符号与一般符号的组合对比,可以看到,数字的无穷性就是一个符号组合的无穷性。
关于信号的编码也是一门专业的学科,作为程序员可以不去追究目前计算机为什么采用这些信息编码格式的原因,但是也至少应该清楚编码的实际含义以及采用这些编码的好处。如果读者对于信息的编码和存储感兴趣,可以自己去更加深入地学习,或许你会在某些领域找到一些更加有效的信息编码方法,让计算变得更快,让存储空间占用更小。 |
上面讲了二进制是什么,其他进制的原理也基本一样。只有在16进制中,出现了符号“A、B、C、D、E、F”分别代表10进制的“10、11、12、13、14、15”,为什么要这样,0-9这几个数字符号与10进制一样,都可以用对应的阿拉伯数字可以表示,因此与10进制共用这些数字符号,但是超过10时,没有相应的阿拉伯数字对应,因此必须用其他符号来表示,而且是单一的符号,而不是组合符号。这样就产生了用“A”来表示10,为什么不用其他符号来表示,这仅是一个约定俗成,并没有其他的含义。如果最初制定这个规则的人,心血来潮用“O、P、Q、R、S、T”来表示一样也是可以的,但是人类的思维总是应该遵从一定的规律,A属于字母排序中的第一位,自然而然地就优先被采用了。
从二进制的表示符号来看,仅包含“0”和“1”,而且任意长度符号组合又可表示无穷多的含义,因此采用二进制来表示信息,不仅可以满足要求,而且让实现变得更加简单可靠。只要用电的有无来表示0和1就可以实现对所有信息的表示。
当然,在计算机领域,对于信号的表达也并不仅采用电信号,任何可以转化成电信号的载体均可以用来承载信息,如我们常见的光盘、磁盘。但是可以说,在计算机领域,数字化的信号表达基本是采用二进制来存储和传输的。
而二进制似乎有长度规定,一般取8位、16位、32位、64位。关于这一点,其实只是因为计算机中表示信息是采用8的整数倍个位来表示,并不是二进制的规定。二进制与十进制一样可以表示任何的有理数。但是二进制可操作性似乎比十进制差,这一点又是一个错觉,我们现在接触的阿拉伯数字0到9,是一个外来品,我们觉得它好记数,是因为我们从小就习惯了它,以致于我们的程序员,在看到一个二进制或16进制的计算时,总是要先转换成10进制才能口算。如果你与老人聊天,或许听说过,我们国家以前的称重量的称是16两一斤的(所谓半斤八两),也就是说是16进制计数的。所以,对于数制,习惯成自然,没有什么先进不先进之分。
再回到2+3=5的问题上来,我们清楚了二进制的概念,那么我们,用二进制来表达下这个需求吧
10b + 11b = 101b
注意,上试中的每个数字后面用了一个字符“b”,表示是一个二进制表示的数。这也是一个约定俗成。没有什么特别含义,因为我们在写程序的时候,在C语言,汇编中常常采用这样的书写规范,编译器就会知道这是一个二进制数。同样在每个数字前面加上“0x”表示一个16进制数。如0x2+0x3=0x5。
注意信息被编码存储在存储介质上后,并不会指明编码本身的含义,就如我们单独把一个字母拿出来看的时候,并没有确切的含义,如一个8位的二进制数1100001b,你可以解释成是一个整数“97”,也可以解释成一个字母“a”。因此对于信号的编码来说,其本身并没有意义,而是使用的人赋予了它含义,这就如文字本身并没有什么意义,而是我们的约定俗成让语言有了具体的含义。 |
为什么程序员要了解二进制的概念,为什么要学习关于计算机的符号表达方法,似乎我们可以什么都不用了解,只要写个程序就解决问题,又何必在这里啰嗦呢?如果你仅仅满足于用现有的知识解决有限的问题,当然就没必要去了解计算机更基础的内部运作原理。但是那样,你可能因为某个程序语言的退出市场而失业,或者因为技术的更新换代而疲于奔命地学习新技术。如果我们理解事情的起因,解决方法,那么即使技术的再怎么日新月异,其基本的思维方式是不变的,学习新的东西自然就变得简单了。
我们首先并不讲解怎样通过计算机的高级程序语言来解决问题,我首先讲解从底层硬件怎么解决问题。经济是政治的基础,硬件是软件的基础,似乎世界就是类似的,这就是哲学。政治与经济的发展是互相作用的,而软件与硬件的发展也是从来分不开的。一个好的工程师,往往需要同时了解硬件和软件两个方面的内容。当把软件固化到硬件中的时候,你也很难划分硬件与软件的界限。而一些知识你也无法简单地划归到硬件知识还是软件知识,就如上面讲到的关于信息编码的一些概念。
我们用一个简单的加法器电路来实现2+3=5,至于加法器电路怎么实现,那需要读者去了解电子电路中关于数字电路的原理部分,如果你有的好奇心很强,你可以一直向更基本的物理规律去追究,如从加法器向下探索,就会涉及到逻辑电路的实现,如触发器、稳态、暂态等诸多的概念,而再向下探索就可以涉及到三极管、电容等电子元件的原理与特性,如果你的好奇心还想探个究竟,就会涉及到原子结构及相互作用的相关理论,不够一般也就了解到电子的运动规律为止,对于量子物理,我想跟你理解计算机的运作和编写程序没有太多关系。
我们做10进制计算时的有效方法就是列一个计算式:
12345
+ 235
----------
= 12580
按照位对齐,从个位开始计算。
那么二进制的2+3=5也应该可以采用同样的方法
10
+ 11
------------
= 101
一样采用位对齐,从低位开始计算,进位方式似同十进制。
那么我们是否可以采用一个类似的电路来实现呢
在这里上传比较费力,有兴趣还是直接到百度文库中看吧。
http://wenku.baidu.com/view/e05b8573f46527d3240ce0bb.html