从事程序开发工作已经好几年,在这期间我参加过不少面试,也面过别人,我一直在思考,什么样的人,才算是人才;什么样的程序员,才能算得上是优秀的程序员?
       直到我这次为了参加CM的面试准备复习一下C++和Linux,想起以前做的各种项目,想起以前编写的各种程序,想起自己一直引以为傲的优势,我才豁然开朗,得知这些问题的答案,原来所有的关键,只在人与人之间不同的思维。不同的学习经历,不同的认知程度,不同的观察角度,都会导致不同的思维方式。
       由于这个问题引述开来,涉及的方面非常之多,我也打算在今后的日子里,写一本关于这方面的心得。但介于篇幅所限,今天暂且只谈算法方面的思维。
       鉴于我经验有限,我不敢说以下我所描述的就是真理,但却是我内心真正思考问题的一种方式。我希望能起到抛砖引玉的作用,可以和大家更深层次的交流。

1.工具是死的,思维是活的。
       从我多年前找到了自己的追求,开始深入学习嵌入式那时起,我就一直认为,你做一件事情所组织的万事万物都是死的,只有你的支配意识才是活的。正因为有这种思路,我从来不勉强自己记任何自己不想记的东西,也从来不看那些看不懂的书。也许有些人对我的学习方法嗤之以鼻,但是事实证明,我记得的东西都是最关键的核心,不记得的随手翻书可查,也是因为这样我才节省了自己大量的精力;设计方案时,我的思维没有受过任何污染和限制,我可以设计出更加有效率的方案。当然,我不敢说我设计的就是最好的,但我一直在进步。
       我大学时,把自己的精力都花在学习新知识上,我不停看各种方面的书籍,像蚊子一样汲取着嵌入式的血液。为什么呢,因为我害怕自己懂得太少,害怕找工作时失去了核心竞争力。然而到现在,就算我接手一个完全陌生领域的项目,我会泰然自若的去学习这个陌生领域的知识(也就是工具),然后剥析思维,设计方案。只要你掌握了思维的命脉,换一种语言,换一个平台,又有何干呢?当然,对于一个初学者而言,从量到质的变化过程是必须的。

2.什么是思维?
       什么是思维?说白了就是你是怎么想的。但在嵌入式的世界里,我的理解是:思维就是你要告诉机器你是怎么想的,然后让机器自己去想。这前后两句分别也代表两种层次,下面听我慢慢阐述。因为是在回忆我以前公交查询系统这个项目时才总结的这篇文章,所以下面我都以这个项目为例。
       假如我们要解决下面这个问题:我们有一个城市中所有公交线路信息和站台信息,我们如果想知道从一个地点到另外一个地点怎么坐车,而这两个站之间没有直达,要知道怎么换乘,该怎么写程序。
       当我们要设计程序的时候,很明显我们会把这个程序的逻辑关系在脑子里面想一遍。经验不足的程序员,会直接拿编程语言去想,这说明你的脑子已经被语言的条条框框毒害了。稍有经验的程序员,会在纸上画好逻辑框图,设计程序。这种方法不能说不好,因为我也经常使用。然而最有效的设计方式,一种核心思路,就是,你一定要知道自己是怎么想的!
       从计算机发明之日起,这个东西就是用来解决人类懒得解决的问题的。所以计算机解决问题的根本思路,就是跟随人的思维。(当然因为机器和人不同,这条也有局限性,后面再提)
现在我们要机器解决换乘的问题,首先我们要问自己是怎么解决的。好吧,闭上眼睛,把计算机中所有的数据都搬到你的脑子中,去想吧。
       在我的脑子里,站点就变成了一个点,公交线路就变成了一条一条的线。我们要换乘,最核心的就是找到换乘地点。说白了,就是分别过两个点,画两条交叉的线,交叉点就是换乘点。这就是我脑子所想的。
       我不清楚我自己所想的是否正确,但是我知道这种方法伴随我解决了很多麻烦的问题,我也知道,随着你的经验增加,你的思维方式一定会进步。你可以花大量时间去看关于各种算法和数据结构的书籍,但是你一定要记住,它们都是工具,你可以丰富工具,但是核心只在你的脑子里。
       好了,现在我们已经告诉了自己,自己(人)是怎么想的。那么我们将自己的想法告诉机器吧。(这里就要看你的编程功底了)。首先,我们将第一个站点的所有过站线路都列出来。其次,我们将第二个站点的过站线路都列出来。最后,我们逐一各从两部分线路数据中挑一条线路出来计算这两条线路是否交叉,交叉点在哪里(用一个入口参数是两条线路的函数完成)。当我们把这两组数据都挑完的时候,程序就完成了。(程序就这样完了?没有,后面细节再提)。
到目前为止,我向各位表述的,就是前面提到的一句话“你要告诉机器你是怎么想的”。为什么我这里强调一定要把看问题的角度提高到思维的层次呢。因为在设计问题的过程中,你可以画各种各样的逻辑框图,你可以想各种各样的语言,但是只有你抓住了自己(人)思考问题的方式,你在写程序的过程中你才不会乱。你可以用各种各样的方法去优化你的程序,改变你的算法(也就是“让机器自己去想”)。但是,当你混乱了逻辑,颠倒了是非时,你依然可以回到最初,想想当初是怎么用人类的思维去思考这个问题时,你依然会豁然开朗。

3.限制条件/成立条件
       其实限制条件和成立条件在前一节中无形提到,是我们在用人的思维思考问题的关键点,比如上面我们提到找换乘方案,那么交叉就是成立条件。又比如如果我们要求找一条最近的换乘路线,那么当我们找出所有的线路之后,线路长度最短就是限制条件。清晰的想好限制条件和成立条件,有助于我们将思维变成程序。

4.优化
       所谓优化,就是前面提到的“让机器自己去想”,就是说我们写的程序,一定是符合计算机特性的。前面说过,计算机是为了代替人而产生。但毕竟计算机和人不同。人的优势是思维,人的思考能力,人的关联能力,匹配能力是计算机没办法比的;而计算机的计算能力,存储能力,规则能力,也是人没办法比的。所以我们可以用人的思考问题,但是一定用机器的思维解决问题(写程序)。打个比方,前面我们有提到一个函数,就是计算两个线路是否有交叉点。如果是人,计算两个线路是否有交叉点,那么人会去看两个线路中是否有同一个站点,这就是人的思维。对于机器而言,程序可以写成和人想的一样,去顺次查找这两个线路中是否有同一个站。然而这样的程序对于机器是没有优势的。我们可以想一想,线路和站点的信息对于人而言是有很多,然而对于计算机而言,这很简单。所以我们可以在程序初始化的时候,就将所有站点的交叉信息存储起来,只要输入一条站点信息,和它交叉的站点就会查出来。这样,我们这个原本是以计算为主体的函数,就变成了以查询为主题的函数,我们知道,对于机器而言,查询比计算快多了。这是一个拿计算机的存储能力来替换人的思考能力的例子。这也就是所谓的“让机器自己去想”。

5.纠错
       实践是检验真理的唯一标准,没有经过实践认证的程序是不可靠的。所以我们一定要知道对自己的程序进行纠错。对于纠错的方法,有很多,我目前也是一个学习者,也在摸索着增长经验,所以我这里只跟大家讲纠错的思维,而不是方法,因为方法我也掌握得不全,不敢班门弄斧。
       对于错误,如果按照思维(逻辑)分类,有两种。一种是软性错误,也就是非逻辑性错误。比如我们在做公交查询信息的时候,如果将公交站台信息用拼音头字母来代替以区分。本来这没有什么问题,结果我们发现有些站台的拼音头字母是相同的,于是就会出错。这种错误,不存在逻辑性,我们只需要将头字母信息后面在简单加个序号区分就可以了。非逻辑性错误不管是调试或者修改都会比较容易。
       另外一种,是硬性错误,也就是逻辑性错误。按照前面的思维转换方法,硬性错误有如下几种:
       1.人本身的逻辑有缺陷,即这个问题从一开始就没有想全面。对于这种错误,我们需要重新构思我们的思维,我们要思考,我们是否有些限制条件或者成立条件没有考虑进去。因为我们人类的成长环境导致我们对很多逻辑都觉得“理所当然”,然而计算机是一个什么都要“规定”的系统。所以这种错误占大多数。
这种错误的调试和修改显得比较复杂,这里只提供一种调试法则:当出现不可解决的问题时,一定要将原本认为肯定正确的“真理”先全部否定,重新思考一遍。这条思维不仅仅是用在程序方面,在很多科研领域甚至我们的生活中,都可以用到这条法则。
       2.人的思维往计算机的思维转换时出现的不匹配错误。
人是一个模糊体,人的一切认知都是以模糊为基础的,人的成长是一个由完全模糊到局部逐渐精确的过程。比如说,我们看到一个人,觉得他长得像我们一个朋友,这个“像”,是个模糊的概念,在你的脑海里,一开始肯定不会去精细匹配这两个人是否“相同”,你只是觉得“像”。当你有第一印象之后,你才会去精细匹配:这两个人在同一个部位是不是都有一粒痣啊,这两个人是不是都是鹰钩鼻啊之类的。
       而计算机正好相反,计算机是一个精确体,计算机的程序就是要将精确变为模糊的过程。在计算机中,所有的东西必须都有“规定”。比如做一个人脸识别系统,你一定要告诉计算机要匹配什么,怎么样才叫“匹配”,百分之几的量才叫“像”,百分之几的量才叫“相同”。正因为计算机最初出现是为了解决人类懒得解决的问题,所以计算机是由精确向模糊靠拢的实现体。但有些领域,计算机不一定要“模糊”,这是因为随着科技日新月异的发展,很多计算机系统的出现并不一定是为了解决人的问题,有时是为了解决计算机自己的问题。鉴于这么学问太高深,我们这里只谈到这个深度。
       言归正传,因为人的思维和计算机的思维不同,所以转换时必然会出现不匹配的问题。最简单的例子,就是浮点运算。对于人而言,要计算,我们可以一直算下去,不计算,我们就可以把后面的位数模糊掉。但是对于计算机而言,这就是大问题了,一是计算机原则上不会四舍五入,而是随着位数增加,整个计算机的复杂程度也要增加。所以根据计算复杂程度挑选嵌入式平台有时也是个很关键的问题。
       当出现这类问题的时候,我只能说解决这个问题完全与程序员的功底相关了。一个程序员,如果完全熟知了计算机的规则,完全可以做到用计算机的思维去思考问题,把计算机的规则变为自己的规则,(也就是人机合一),那么这个问题出现的概率就会减小。这也是我们学习进步的方向。
       3.计算机的逻辑错误。按照道理来讲,计算机是不会出现逻辑错误的,因为计算机永远都是“正确的”,它对于自己而言,是完全可靠可知的。然而因为嵌入式的特殊性,因为嵌入式是和硬件紧密结合的,嵌入式程序员需要考虑很多通用计算机程序员不需要操心的事情,所以我这里不得不提一下计算机的逻辑错误。
       什么是计算机逻辑?这里我不清楚我的表述方法是否正确,但是我用这个名词,想表达的意思是:计算机在自己内部执行指令,访问接口就是计算机的逻辑。我们做嵌入式系统,要跟各种各样的接口打交道,我们一定要熟知计算机的逻辑。比如我们新接触一个芯片,那么这个芯片有AD转换模块,它进行AD转换都是怎么实现的,编程要注意些什么。这都是计算机逻辑。
       如果细细分辨,也许计算机逻辑错误可以归入一类或者二类,因为计算机错了,实际就是人错了,人不了解计算机而已。而且计算机的各种接口实现,实在也说不上属于算法的一部分。然后因为这个类在嵌入式中实在很重要,所以我将其单独归为一类。要解决这个计算机逻辑错误是很简单的。这就跟你的细致,你的学习方法有关系了。每接触一种芯片,你是否有认真的读过他的官方资料?每接触一种芯片,你是否有横向比较过它与其他处理器的不同点?这些都是合格的嵌入式程序员需要总结和学习的。

结语:时间所限,本文写到这里,也该差不多了,计算机真的是一门高深而有趣的学问,本文所描述的也只是我自己比较片面的想法而已。不能不说还有很多未知的领域需要我们去探索学习。我不敢强迫大家都跟我一样,注重思维。但我一直认为思维是创造和创新的根本。我也希望本文能给大家一个启发,在今后的学习和工作过程中,能够总结自己的理论和认知,让自己的能力更上一层楼。谢谢大家!