|
2005年11月26日
在李开复博士加入Google公司并担任google中国总裁后,最近他们推出了一项Google Code Jam - 中国编程挑战赛,为了更好的了解这次大赛的目的和背后的一些想法,我们约到了李开复博士进行了专访。
记者:那这次程序大赛主要是为了考察程序员哪些素质?
李开复:我们出的题目,需要选手具备很缜密的算法、严格的过程,完善的测试,容错和速度也需要考虑的因素,而且我们的题目可能比较困难一些。
比赛考察的重点是选手有没有把计算机科学学好。我们发现,很多学生只看表面的东西,其实更重要的是学懂理论。我原来就说过外功和内功的差别,外功很好的人去打擂台,肯定打不过一个内功很好的。这里的内功就是算法,数据结构,操作系统,数据库方面的基础知识,要把大一,大二,大三这些基础课程学的特别扎实,把这些课程学好。一些学生号称自己知道很多最新的技术,但这些只是一些语言。就像一个人学会了几十种语言,却写不出一篇好文章。
当然,我们考察的还有创新能力,要选手反应要够快,想事情要严谨。有一个挑战赛的环节,是别人可以找你程序中的bug。因为当程序员真的去工作之后,有几件事情是每个程序员都要去做的。比如测试和codereview。如果只会写程序而不会测试是不行的,而且也不能只看懂自己的代码看不懂别人的代码。这次比赛我们是引入了一种竞争的机制,需要靠给别人挑错来给自己加分,以后可能有更好的方式来测试选手这方面的能力。
记者:您希望通过大赛向程序员传递怎样的信息?
李开复:中国有一些现象,比如一个程序员作好了就是做经理、总经理、创业。程序员并没有认为他们从事的是一项非常了不起的事业,大家觉得做科学家,数学家很好,但提到程序员就不行了。但google有很多优秀的程序员,他们甚至到副总裁级别,他们也不要带团队,就是整天编程。Google的No.1的程序员叫Jeffrey Dean,他做了一件非常了不起事情,开发了一个技术MapReduce可以让程序员很容易的学会为几千台服务器编程。他现在38岁了,可能按照中国的观点来说他可能不适合编程了,但这种观念必须要改掉。
所以我们希望更多人能够理解做终身的程序员是一个好事情,并不是一个不好的事情。
记者:我们看到很多报道Google的产品开发往往只有一个很小的团队进行的。联想到这次大赛也是一种针对个人的比赛形式,是否您认为小团队也是软件开发的一种趋势呢?
李开复:团队精神的确非常重要,但不是我们这次比赛的目标。不过最后得奖的50个人我们会带他们出去一个地方,做一些很有趣的事情。这些事情也会间接的培养他们的团队。
其实,我觉得互联网又给了个人一个新的发展机会。google很多新产品就是一个人有了新的idea,于是实践一下,然后把产品开放出来看效果,并且可以实时的得到回馈。它是将互联网当作实验室,因为是free的软件,有不足的地方用户也可以接受。我发现在Google,即便是在这个idea要继续产品化的时候,也不过二三个人,最后变成一个很大的产品,也不过二三十个人的团队。
大的队伍进行开放也是有代价的,100个人做一个软件由于要不断的会议、交流、作好的功能取消这些会丧失掉80%的效率。或许1000个人一些做事只能做200个人所作的事情,但200个人如果不需要任何磨合,甚至可以做1000个人的事情。
记者:Google在中国的招聘全部来自国内吗?
李开复:我们这次比赛和招聘不是一回事,当然成功的选手可能会加入Google,但这不是刻意的。我们主要是为了提升对程序员的尊敬,理解程序员是一个终身的职业,同时把我们的理念表现出来。
在国内的招聘方面,我们有很大的特色,比如不要求英文能力,这些能力可以要求他们以后提高。同时,会有一大批从总部回来的中国人,比例可能会是5:1,这边每招5个人,对应着国外回来的1个人。这样可以有效的将google总部的文化带回来。当然,Google中国的招聘也是同样严格的,没有放松任何要求。
记者:有消息说google招人一半是博士,他们偏学术化还是工程化?Google中国做的事情偏科研还是偏产品呢?
李开复:这两种博士都有,纯学术化的我们也有,但更多的是要具有实战经验的。我们雇的博士绝大部分也要做程序员,并不会因为是博士,就可以来分配工作给程序员。大家都是程序员。他们必须要编程,如果只是写几个公式,分配给别人编程,不是说这种不好,而是不适合Google公司。Google的理念是每个工程师都是科学家,每个科学家都是工程师。我们要做创新,但也有编程。
另外,我对中国程序员这些年来的进步感到非常惊讶,这7年来的进步很多。我想这次找到符合我们标准的人的比例要高10-20倍。应该说,计算机专业学生并没有增加很多,但水平增高了很多,这里有教育方面提高的原因,但更多是互联网的效果,程序员可以在网上学习,参加竞赛,有很多类似csdn这样网站和交流平台。而且,pc越来越便宜,高中就可能可以购买一台使用。而且很多竞赛也让中国的程序员树立了信息,基于这些因素,我对google在中国的发展有很大的信心。
我想,应该再过几年,我们就不需要专门在中国举行这种编程比赛了,因为可以纳入全球的比赛。现在因为各种因素到国外比赛还不太方便,于是我们现在提供给程序员这个机会可以先接触一下这方面的大赛。(《程序员》杂志闫辉)
2005年11月16日
-----在计算机的学校过程中,发现sir先生的文章真的很好,对我影响很大,所以就转贴过来了
计算机科学与技术这一门科学深深的吸引着我们这些同学们,上计算机系已经有近三年了,自己也做了一些思考,我一直认为计算机科学与技术这门专业,在本科阶段是不可能切分成计算机科学和计算机技术的,因为计算机科学需要相当多的实践,而实践需要技术;每一个人(包括非计算机专业),掌握简单的计算机技术都很容易(包括程序设计),但计算机专业的优势就在于,我们掌握许多其他专业并不“深究”的东西,例如,算法,体系结构,等等。非计算机专业的人可以很容易地做一个芯片,写一段程序,但他们做不出计算机专业能够做出来的大型系统。(与司徒彦南兄的谈话)今天我想专门谈一谈计算机科学,并将重点放在计算理论上。 计算机理论的一个核心问题--从数学谈起: 记得当年大一入学,每周六课时高等数学,天天作业不断(那时是六日工作制)。颇有些同学惊呼走错了门:咱们这到底念的是什么系?不错,你没走错门,这就是计算机科学与技术系。我国计算机科学系里的传统是培养做学术研究,尤其是理论研究的人(方向不见得有问题,但是做得不是那么尽如人意)。而计算机的理论研究,说到底了,如网络安全,图形图像学,视频音频处理,哪个方向都与数学有着很大的关系,虽然也许是正统数学家眼里非主流的数学。这里我还想阐明我的一个观点:我们都知道,数学是从实际生活当中抽象出来的理论,人们之所以要将实际抽象成理论,目的就在于想用抽象出来的理论去更好的指导实践,有些数学研究工作者喜欢用一些现存的理论知识去推导若干条推论,殊不知其一:问题考虑不全很可能是个错误的推论,其二:他的推论在现实生活中找不到原型,不能指导实践。严格的说,我并不是一个理想主义者,政治课上学的理论联系实际一直是指导我学习科学文化知识的航标(至少我认为搞计算机科学与技术的应当本着这个方向)。 其实我们计算机系学数学光学高等数学是不够的(典型的工科院校一般都开的是高等数学),我们应该像数学系一样学一下数学分析(清华计算机系开的好像就是数学分析),数学分析这门科学,咱们学计算机的人对它有很复杂的感情。在于它是偏向于证明型的数学课程,这对我们培养良好的分析能力极有帮助。我的软件工程学导师北工大数理学院的王仪华先生就曾经教导过我们,数学系的学生到软件企业中大多作软件设计与分析工作,而计算机系的学生做程序员的居多,原因就在于数学系的学生分析推理能力,从所受训练的角度上要远远在我们之上。当年出现的怪现象是:计算机系学生的高中数学基础在全校数一数二(希望没有冒犯其它系的同学),教学课时数也仅次于数学系,但学完之后的效果却不尽如人意。难道都是学生不努力吗,我看未见得,方向错了也说不一定,其中原因何在,发人深思。 我个人的浅见是:计算机系的学生,对数学的要求固然跟数学系不同,跟物理类差别则更大。通常非数学专业的所谓“高等数学”,无非是把数学分析中较困难的理论部分删去,强调套用公式计算而已。而对计算机系来说,数学分析里用处最大的恰恰是被删去的理论部分。说得难听一点,对计算机系学生而言,追求算来算去的所谓“工程数学”已经彻底地走进了误区。记上一堆曲面积分的公式,难道就能算懂了数学?那倒不如现用现查,何必费事记呢?再不然直接用 Mathematics或是Matalab好了。 我在系里最爱做的事情就是给学弟学妹们推荐参考书。中文的数学分析书,一般都认为以北大张筑生老师的“数学分析新讲”为最好。万一你的数学实在太好,那就去看菲赫金哥尔茨的“微积分学教程”好了--但我认为没什么必要,毕竟你不想转到数学系去。吉米多维奇的“数学分析习题集”也基本上是计算型的东东。书的名气很大,倒不见得适合我们,还是那句话,重要的是数学思想的建立,生活在信息社会里我们求的是高效,计算这玩意还是留给计算机吧。不过现在多用的似乎是复旦大学的《数学分析》也是很好的教材。 中国的所谓高等代数,就等于线性代数加上一点多项式理论。我以为这有好的一面,因为可以让学生较早感觉到代数是一种结构,而非一堆矩阵翻来覆去。这里不得不提南京大学林成森,盛松柏两位老师编的“高等代数”,感觉相当舒服。此书相当全面地包含了关于多项式和线性代数的基本初等结果,同时还提供了一些有用的又比较深刻的内容,如Sturm序列,Shermon- Morrison公式,广义逆矩阵等等。可以说,作为本科生如能吃透此书,就可以算高手。国内较好的高等代数教材还有清华计算机系用的那本,清华出版社出版,书店里多多,一看就知道。从抽象代数的观点来看,高等代数里的结果不过是代数系统性质的一些例子而已。莫宗坚先生的《代数学》里,对此进行了深刻的讨论。然而莫先生的书实在深得很,作为本科生恐怕难以接受,不妨等到自己以后成熟了一些再读。 正如上面所论述的,计算机系的学生学习高等数学:知其然更要知其所以然。你学习的目的应该是:将抽象的理论再应用于实践,不但要掌握题目的解题方法,更要掌握解题思想,对于定理的学习:不是简单的应用,而是掌握证明过程即掌握定理的由来,训练自己的推理能力。只有这样才达到了学习这门科学的目的,同时也缩小了我们与数学系的同学之间思维上的差距。 概率论与数理统计这门课很重要,可惜大多数院校讲授这门课都会少些东西。少了的东西现在看至少有随机过程。到毕业还没有听说过Markov 过程,此乃计算机系学生的耻辱。没有随机过程,你怎么分析网络和分布式系统?怎么设计随机化算法和协议?据说清华计算机系开有“随机数学”,早就是必修课。另外,离散概率论对计算机系学生来说有特殊的重要性。而我们国家工程数学讲的都是连续概率。现在,美国已经有些学校开设了单纯的“离散概率论”课程,干脆把连续概率删去,把离散概率讲深些。我们不一定要这么做,但应该更加强调离散概率是没有疑问的。这个工作我看还是尽早的做为好。 计算方法学(有些学校也称为数学分析学)是最后一门由数理学院给我们开的课。一般学生对这门课的重视程度有限,以为没什么用。不就是照套公式嘛!其实,做图形图像可离不开它,密码学搞深了也离不开它。而且,在很多科学工程中的应用计算,都以数值的为主。这门课有两个极端的讲法:一个是古典的 “数值分析”,完全讲数学原理和算法;另一个是现在日趋流行的“科学与工程计算”,干脆教学生用软件包编程。我个人认为,计算机系的学生一定要认识清楚我们计算机系的学生为什么要学这门课,我是很偏向于学好理论后用计算机实现的,最好使用C语言或C++编程实现。向这个方向努力的书籍还是挺多的,这里推荐大家高等教育出版社(CHEP)和施普林格出版社(Springer)联合出版的《计算方法(Computational Methods)》, 华中理工大学数学系写的(现华中科技大学),这方面华科大做的工作在国内应算是比较多的,而个人认为以这本最好,至少程序设计方面涉及了:任意数学函数的求值,方程求根,线性方程组求解,插值方法,数值积分,场微分方程数值求解。李庆扬的那本则理论性过强,与实际应用结合得不太紧。 每个学校本系里都会开一门离散数学,涉及集合论,图论,和抽象代数,数理逻辑。不过,这么多内容挤在离散数学一门课里,是否时间太紧了点?另外,计算机系学生不懂组合和数论,也是巨大的缺陷。要做理论,不懂组合或者数论吃亏可就太大了。从理想的状态来看,最好分开六门课:集合,逻辑,图论,组合,代数,数论。这个当然不现实,因为没那么多课时。也许将来可以开三门课:集合与逻辑,图论与组合,代数与数论。(这方面我们学校已经着手开始做了)不管课怎么开,学生总一样要学。下面分别谈谈上面的三组内容。 古典集合论,北师大出过一本《基础集合论》不错。 数理逻辑,中科院软件所陆钟万教授的《面向计算机科学的数理逻辑》就不错。现在可以找到陆钟万教授的讲课录像,http://www.cas.ac.cn/html/Dir/2001/11/06/3391.htm自己去看看吧。总的来说,学集合/逻辑起手不难,普通高中生都能看懂。但越往后越感觉深不可测。 学完以上各书之后,如果你还有精力兴趣进一步深究,那么可以试一下GTM系列中的《Introduction to Axiomatic Set Theory》和《A Course of Mathematical Logic》。这两本都有世界图书出版社的引进版。你如果能搞定这两本,可以说在逻辑方面真正入了门,也就不用再浪费时间听我瞎侃了。 据说全中国最多只有三十个人懂图论。此言不虚。图论这东东,技巧性太强,几乎每个问题都有一个独特的方法,让人头痛。不过这也正是它魅力所在:只要你有创造性,它就能给你成就感。我的导师说,图论里面随便揪一块东西就可以写篇论文。大家可以体会里面内容之深广了吧!国内的图论书中,王树禾老师的“图论及其算法”非常成功。一方面,其内容在国内教材里算非常全面的。另一方面,其对算法的强调非常适合计算机系(本来就是科大计算机系教材)。有了这本书为主,再参考几本翻译的,如Bondy & Murty的《图论及其应用》,人民邮电出版社翻译的《图论和电路网络》等等,就马马虎虎,对本科生足够了。再进一步,世界图书引进有GTM系列的 "Modern Graph Theory"。此书确实经典!国内好象还有一家出版了个翻译版。不过,学到这个层次,还是读原版好。搞定这本书,也标志着图论入了门。 离散数学方面北京工业大学实验学院有个世界级的专家,叫邵学才,复旦大学概率论毕业的,教过高等数学,线性代数,概率论,最后转向离散数学,出版著作无数,论文集新加坡有一本,堪称经典,大家想学离散数学的真谛不妨找来看看。这老师的课我专门去听过,极为经典。不过你要从他的不经意的话中去挖掘精髓。在同他的交谈当中我又深刻地发现一个问题,虽说邵先生写书无数,但依他自己的说法每本都差不多,我实在觉得诧异,他说主要是有大纲的限制,不便多写。这就难怪了,很少听说国外写书还要依据个什么大纲(就算有,内容也宽泛的多),不敢越雷池半步,这样不是看谁的都一样了。外版的书好就好在这里,最新的科技成果里面都有论述,别的先不说,至少是“紧跟时代的理论知识”。 组合感觉没有太适合的国产书。还是读Graham和Knuth等人合著的经典“具体数学”吧,西安电子科技大学出版社有翻译版。 抽象代数,国内经典为莫宗坚先生的“代数学”。此书是北大数学系教材,深得好评。然而对本科生来说,此书未免太深。可以先学习一些其它的教材,然后再回头来看“代数学”。国际上的经典可就多了,GTM系列里就有一大堆。推荐一本谈不上经典,但却最简单的,最容易学的:http://www.math.miami.edu/~ec/book/这本“Introduction to Linear and Abstract Algebra"非常通俗易懂,而且把抽象代数和线性代数结合起来,对初学者来说非常理想,我校比较牛的同学都有收藏。 数论方面,国内有经典而且以困难著称的”初等数论“(潘氏兄弟著,北大版)。再追溯一点,还有更加经典(可以算世界级)并且更加困难的”数论导引“(华罗庚先生的名著,科学版,九章书店重印,繁体的看起来可能比较困难)。把基础的几章搞定一个大概,对本科生来讲足够了。但这只是初等数论。本科毕业后要学计算数论,你必须看英文的书,如Bach的"Introduction to Algorithmic Number Theory"。 计算机科学理论的根本,在于算法。现在很多系里给本科生开设算法设计与分析,确实非常正确。环顾西方世界,大约没有一个三流以上计算机系不把算法作为必修的。算法教材目前公认以Corman等著的"Introduction to Algorithms"为最优。对入门而言,这一本已经足够,不需要再参考其它书。 再说说形式语言与自动机。我看过北邮的教材,应该说写的还清楚。但是,有一点要强调:形式语言和自动机的作用主要在作为计算模型,而不是用来做编译。事实上,编译前端已经是死领域,没有任何 open problems,北科大的班晓娟博士也曾经说过,编译的技术已相当成熟。如果为了这个,我们完全没必要去学形式语言--用用yacc什么的就完了。北邮的那本在国内还算比较好,但是在深度上,在跟可计算性的联系上都有较大的局限,现代感也不足。所以建议有兴趣的同学去读英文书,不过国内似乎没引进这方面的教材。可以去互动出版网上看一看。入门以后,把形式语言与自动机中定义的模型,和数理逻辑中用递归函数定义的模型比较一番,可以说非常有趣。现在才知道,什么叫“宫室之美,百官之富”! 计算机科学和数学的关系有点奇怪。二三十年以前,计算机科学基本上还是数学的一个分支。而现在,计算机科学拥有广泛的研究领域和众多的研究人员,在很多方面反过来推动数学发展,从某种意义上可以说是孩子长得比妈妈还高了。但不管怎么样,这个孩子身上始终流着母亲的血液。这血液是the mathematical underpinning of computer science(计算机科学的数学基础),也就是理论计算机科学。原来在东方大学城图书馆中曾经看过一本七十年代的译本(书皮都没了,可我就爱关注这种书),大概就叫《计算机数学》。那本书若是放在当时来讲决是一本好书,但现在看来,涵盖的范围还算广,深度则差了许多,不过推荐大一的学生倒可以看一看,至少可以使你的计算数学入入门。 最常和理论计算机科学放在一起的一个词是什么?答:离散数学。这两者的关系是如此密切,以至于它们在不少场合下成为同义词。(这一点在前面的那本书中也有体现)传统上,数学是以分析为中心的。数学系的同学要学习三四个学期的数学分析,然后是复变函数,实变函数,泛函数等等。实变和泛函被很多人认为是现代数学的入门。在物理,化学,工程上应用的,也以分析为主。 随着计算机科学的出现,一些以前不太受到重视的数学分支突然重要起来。人们发现,这些分支处理的数学对象与传统的分析有明显的区别:分析研究的问题解决方案是连续的,因而微分,积分成为基本的运算;而这些分支研究的对象是离散的,因而很少有机会进行此类的计算。人们从而称这些分支为“离散数学”。“离散数学”的名字越来越响亮,最后导致以分析为中心的传统数学分支被相对称为“连续数学”。 离散数学经过几十年发展,基本上稳定下来。一般认为,离散数学包含以下学科 : 1) 集合论,数理逻辑与元数学。这是整个数学的基础,也是计算机科学的基础。 2) 图论,算法图论;组合数学,组合算法。计算机科学,尤其是理论计算机科学的核心是 算法,而大量的算法建立在图和组合的基础上。 3) 抽象代数。代数是无所不在的,本来在数学中就非常重要。在计算机科学中,人们惊讶地发现代数竟然有如此之多的应用。 但是,理论计算机科学仅仅就是在数学的上面加上“离散”的帽子这么简单吗?一直到大约十几年前,终于有一位大师告诉我们:不是。 D.E.Knuth(他有多伟大,我想不用我废话了)在Stanford开设了一门全新的课程Concrete Mathematics。 Concrete这个词在这里有两层含义: 首先:对abstract而言。Knuth认为,传统数学研究的对象过于抽象,导致对具体的问题关心不够。他抱怨说,在研究中他需要的数学往往并不存在,所以他只能自己去创造一些数学。为了直接面向应用的需要,他要提倡“具体”的数学。在这里我做一点简单的解释。例如在集合论中,数学家关心的都是最根本的问题--公理系统的各种性质之类。而一些具体集合的性质,各种常见集合,关系,映射都是什么样的,数学家觉得并不重要。然而,在计算机科学中应用的,恰恰就是这些具体的东西。Knuth能够首先看到这一点,不愧为当世计算机第一人。其次,Concrete是Continuous(连续)加上 discrete(离散)。不管连续数学还是离散数学,都是有用的数学! 理论与实际的结合--计算机科学研究的范畴 前面主要是从数学角度来看的。从计算机角度来看,理论计算机科学目前主要的研究领域包括:可计算性理论,算法设计与复杂性分析,密码学与信息安全,分布式计算理论,并行计算理论,网络理论,生物信息计算,计算几何学,程序语言理论等等。这些领域互相交叉,而且新的课题在不断提出,所以很难理出一个头绪来。想搞搞这方面的工作,推荐看中国计算机学会的一系列书籍,至少代表了我国的权威。下面随便举一些例子。 由于应用需求的推动,密码学现在成为研究的热点。密码学建立在数论(尤其是计算数论),代数,信息论,概率论和随机过程的基础上,有时也用到图论和组合学等。很多人以为密码学就是加密解密,而加密就是用一个函数把数据打乱。这样的理解太浅显了。 现代密码学至少包含以下层次的内容: 第一,密码学的基础。例如,分解一个大数真的很困难吗?能否有一般的工具证明协议正确? 第二,密码学的基本课题。例如,比以前更好的单向函数,签名协议等。 第三,密码学的高级问题。例如,零知识证明的长度,秘密分享的方法。 第四,密码学的新应用。例如,数字现金,叛徒追踪等。 在分布式系统中,也有很多重要的理论问题。例如,进程之间的同步,互斥协议。一个经典的结果是:在通信信道不可靠时,没有确定型算法能实现进程间协同。所以,改进TCP三次握手几乎没有意义。例如时序问题。常用的一种序是因果序,但因果序直到不久前才有一个理论上的结果....例如,死锁没有实用的方法能完美地对付。例如,......操作系统研究过就自己去举吧! 如果计算机只有理论,那么它不过是数学的一个分支,而不成为一门独立的科学。事实上,在理论之外,计算机科学还有更广阔的天空。 我一直认为,4年根本不够学习计算机的基础知识,因为面太宽了,8年,应该差不多了...... 这方面我想先说说我们系在各校普遍开设的《计算机基础》。在高等学校开设《计算机基础课程》是我国高教司明文规定的各专业必修课程要求。主要内容是使学生初步掌握计算机的发展历史,学会简单的使用操作系统,文字处理,表格处理功能和初步的网络应用功能。但是在计算机科学系教授此门课程的目标决不能与此一致。在计算机系课程中目标应是:让学生较为全面的了解计算机学科的发展,清晰的把握计算机学科研究的方向,发展的前沿即每一个课程在整个学科体系中所处的地位。搞清各学科的学习目的,学习内容,应用领域。使学生在学科学习初期就对整个学科有一个整体的认识,以做到在今后的学习中清楚要学什么,怎么学。计算机基本应用技能的位置应当放在第二位或更靠后,因为这一点对于本系的学生应当有这个摸索能力。这一点很重要。推荐给大家一本书:机械工业出版社的《计算机文化》(New Perspective of Computer Science),看了这本书我才深刻的体会到自己还是个计算机科学初学者,才比较透彻的了解了什么是计算机科学。 一个一流计算机系的优秀学生决不该仅仅是一个编程高手,但他一定首先是一个编程高手。我上大学的时候,第一门专业课是C语言程序设计,念计算机的人从某种角度讲相当一部分人是靠写程序吃饭的。在计算机系里一直有这样的争论(时至今日CSDN上也有),关于第一程序设计语言该用哪一种。我个人认为,用哪种语言属于末节,关键在养成良好的编程习惯。当年老师对我们说,打好基础后学一门新语言只要一个星期。现在我觉得根本不用一个星期,前提是先把基础打好。不要再犹豫了,学了再说,等你抉择好了,别人已经会了几门语言了。 汇编语言和微机原理是两门特烦人的课。你的数学/理论基础再好,也占不到什么便宜。这两门课之间的次序也好比先有鸡还是先有蛋,无论你先学哪门,都会牵扯另一门课里的东西。所以,只能静下来慢慢琢磨。这就是典型的工程课,不需要太多的聪明和顿悟,却需要水滴石穿的渐悟。有关这两门课的书,计算机书店里不难找到。弄几本最新的,对照着看吧。组成原理推荐《计算机组成与结构》清华大学王爱英教授写的。汇编语言大家拿8086/8088入个门,之后一定要学80x86汇编语言。实用价值大,不落后,结构又好,写写高效病毒,高级语言里嵌一点汇编,进行底层开发,总也离不开他,推荐清华大学沈美明的《IBM-PC汇编语言程序设计》。有些人说不想了解计算机体系结构,也不想制造计算机,所以诸如计算机原理,汇编语言,接口之类的课觉得没必要学,这样合理吗?显然不合理,这些东西迟早得掌握,肯定得接触,而且,这是计算机专业与其他专业学生相比的少有的几项优势。做项目的时候,了解这些是非常重要的,不可能说,仅仅为了技术而技术,只懂技术的人最多做一个编码工人,而永远不可能全面地了解整个系统的设计,而编码工人是越老越不值钱。关于组成原理还有个讲授的问题,在我学这门课程时老师讲授时把CPU工作原理誉微程序设计这一块略掉了,理由是我们国家搞CPU技术不如别的国家,搞了这么长时间好不容易出了个龙芯比Intel的还差个十万八千里,所以建议我们不要学了。我看这在各校也未见得不是个问题吧!若真是如他所说,那中国的计算机科学哪个方向都可以停了,软硬件,应用,有几项搞得过美国,搞不过别人就不搞了,那我们坐在这里干什么?教学的观念需要转变的。 模拟电路这东东,如今不仅计算机系学生搞不定,电子系学生也多半害怕。如果你真想软硬件通吃,那么建议你先看看邱关源的“电路原理”,也许此后再看模拟电路底气会足些。教材:康华光的“电子技术基础”(高等教育出版社)还是不错的(我校电子系在用)。有兴趣也可以参考童诗白的书。 数字电路比模拟电路要好懂得多。推荐大家看一看我们北工大刘英娴教授写的《数字逻辑》业绩人士都说这本书很有参考价值(机械工业出版社的)。原因很明了,实用价值高,能听听她讲授的课程更是有一种“享受科学”的感觉。清华大学阎石的书也算一本好教材,遗憾的一点是集成电路讲少了些。真有兴趣,看一看大规模数字系统设计吧(北航那本用的还比较多)。 计算机系统结构该怎么教,国际上还在争论。国内能找到的较好教材为Stallings的"Computer Organization and Architecture:Designing for Performance"(清华影印 本)。国际上最流行的则是“Computer architecture: aquantitative approach", by Patterson & Hennessy。 操作系统可以随便选用《操作系统的内核设计与实现》和《现代操作系统》两书之一。这两部都可以算经典,唯一缺点就是理论上不够严格。不过这领域属于 Hardcore System,所以在理论上马虎一点也情有可原。想看理论方面的就推荐清华大学出版社《操作系统》吧,高教司司长张尧学写的,我们教材用的是那本。另外推荐一本《Windows操作系统原理》机械工业出版社的,这本书是我国操作系统专家在微软零距离考察半年,写作历时一年多写成的,教操作系统的专家除了清华大学的张尧学(现高教司司长)几乎所有人都参加了。 Bill Gates亲自写序。里面不但结合windows2000,xp详述操作系统的内核,而且后面讲了一些windows编程基础,有外版书的味道,而且上面一些内容可以说在国内外只有那本书才有对windows内核细致入微的介绍, 如果先把形式语言学好了,则编译原理中的前端我看只要学四个算法:最容易实现的递归下降;最好的自顶向下算法LL(k);最好的自底向上算法LR(k);LR(1)的简化SLR(也许还有另一简化LALR)。后端完全属于工程性质,自然又是another story。 推荐教材:Kenneth C.Louden写的“Compiler Construction Principles and Practice”即是《编译原理及实践》(机械工业出版社的译本) 学数据库要提醒大家的是,会用VFP,VB, Power builder不等于懂数据库。(这世界上自以为懂数据库的人太多了!)数据库设计既是科学又是艺术,数据库实现则是典型的工程。所以从某种意义上讲,数据库是最典型的一门计算机课程--理工结合,互相渗透。另外推荐大家学完软件工程学后再翻过来看看数据库技术,又会是一番新感觉。推荐教材: Abraham Silberschatz等著的 "Database System Concepts".作为知识的完整性,还推荐大家看一看机械工业出版社的《数据仓库》译本。 计算机网络的标准教材还是来自 Tanenbaum的《Computer Networks》(清华大学有译本)。还有就是推荐谢希仁的《计算机网络教程》(人民邮电出版社)问题讲得比较清楚,参考文献也比较权威。不过,网络也属于Hardcore System,所以光看书是不够的。建议多读RFC,http://www.ietf.org/rfc.htm里可以按编号下载RFC文档。从IP的读起。等到能掌握10种左右常用协议,就没有几个人敢小看你了。再做的工作我看放在网络设计上就比较好了。 数据结构的重要性就不言而喻了,学完数据结构你会对你的编程思想进行一番革命性的洗礼,会对如何建立一个合理高效的算法有一个清楚的认识。对于算法的建立我想大家应当注意以下几点: 当遇到一个算法问题时,首先要知道自己以前有没有处理过这种问题.如果见过,那么你一般会顺利地做出来;如果没见过,那么考虑以下问题: 1. 问题是否是建立在某种已知的熟悉的数据结构(例如,二叉树)上?如果不是,则要自己设计数据结构。 2. 问题所要求编写的算法属于以下哪种类型?(建立数据结构,修改数据结构,遍历,查找,排序...) 3. 分析问题所要求编写的算法的数学性质.是否具备递归特征?(对于递归程序设计,只要设计出合理的参数表以及递归结束的条件,则基本上大功告成.) 4. 继续分析问题的数学本质.根据你以前的编程经验,设想一种可能是可行的解决办法,并证明这种解决办法的正确性.如果题目对算法有时空方面的要求,证明你的设想满足其要求.一般的,时间效率和空间效率难以兼得.有时必须通过建立辅助存储的方法来节省时间. 5. 通过一段时间的分析,你对解决这个问题已经有了自己的一些思路.或者说,你已经可以用自然语言把你的算法简单描述出来.继续验证其正确性,努力发现其中的错误并找出解决办法.在必要的时候(发现了无法解决的矛盾),推翻自己的思路,从头开始构思. 6. 确认你的思路可行以后,开始编写程序.在编写代码的过程中,尽可能把各种问题考虑得详细,周密.程序应该具有良好的结构,并且在关键的地方配有注释. 7. 举一个例子,然后在纸上用笔执行你的程序,进一步验证其正确性.当遇到与你的设想不符的情况时,分析问题产生的原因是编程方面的问题还是算法思想本身有问题. 8. 如果程序通过了上述正确性验证,那么在将其进一步优化或简化。 9. 撰写思路分析,注释. 对于具体的算法思路,只能靠你自己通过自己的知识和经验来加以获得,没有什么特定的规律(否则程序员全部可以下岗了,用机器自动生成代码就可以了).要有丰富的想象力,就是说当一条路走不通时,不要钻牛角尖,要敢于推翻自己的想法.我也只不过是初学者,说出上面的一些经验,仅供大家参考和讨论。 关于人工智能,我觉得的也是非常值得大家仔细研究的,虽然不能算是刚刚兴起的学科了,但是绝对是非常有发展前途的一门学科。我国人工智能创始人之一,北京科技大学涂序彦教授(这老先生是我的导师李小坚博士的导师)对人工智能这样定义:人工智能是模仿、延伸和扩展人与自然的智能的技术科学。在美国人工智能官方教育网站上对人工智能作了如下定义:Artificial Intelligence, or AI for short, is a combination of computer science, physiology, and philosophy. AI is a broad topic, consisting of different fields, from machine vision to expert systems. The element that the fields of AI have in common is the creation of machines that can "think". 这门学科研究的问题大概说有: (1)符号主义: 符号计算与程序设计基础,知识表达方法 :知识与思维,产生式规则,语意网络,一阶谓词逻辑问题求解方法:搜索策略,启发式搜寻,搜寻算法,问题规约方法,谓词演算:归结原理,归结过程专家系统:建立专家系统的方法及工具 (2)联接主义(神经网络学派):1988年美国权威机构指出:数据库,网络发展呈直线上升,神经网络可能是解决人工智能的唯一途径。 我想对于人工智能的学习,大家一定不要像学数学似的及一些现成的结论,要学会分析问题,最好能利用程序设计实现,这里推荐给大家ACM最佳博士论文奖获得者涂晓媛博士的著作《人工鱼-计算机动画的人工生命方法》(清华大学出版社)。搞人工生命的同学不会不知道国际知名的涂氏父女吧。关于人工智能的书当然首选《Artificial Intelligence A New Synthesis》Nils J.Nilsson.鼻祖嘛! 关于网络安全我也想在这里说两句,随着计算机技术的发展,整个社会的信息化水平突飞猛进,计算机网络技术日新月异,网络成了当即社会各个工作领域不可缺少的组成部分,只要有网络存在,网络安全问题就是一个必须解决好的问题,学习网络安全不是简简单单的收集一些黑客工具黑一黑别人的网站,而是要学习他的数学原理,实现原理,搞清底层工作机制,这样才能解决大部分的现有问题和新出现的安全问题。 关于计算机科学的一些边缘科学我想谈一谈软件工程技术,对于一个企业,推出软件是不是就是几个程序员坐在一起,你写一段程序,我写一段程序呢?显然不是。软件工程是典型的计算机科学和数学,管理科学,心理学,社会学等学科的综合。它使我们这些搞理论和技术的人进入了一个社会。你所要考虑的不仅仅是程序的优劣,更应该考虑程序与软件的区别,软件与软件产品的区别,软件软件产品的市场前景,如何去更好的与人交流。这方面我还在学习阶段,以后这方面再写文章吧,先推荐给大家几本书:畅销20年不衰的《人月神话》(清华大学中文版,中国电力出版社影印版),《软件工程-实践者研究的方法》(机械工业出版社译本),《人件》(据说每一位微软公司的部门经理都读过这本书,推荐老总们和想当老总的同学都看看,了解一下什么是软件企业中的人)以及微软公司的《软件开发的科学与艺术》和《软件企业的管理与文化》(研究软件企业的制胜之道当然要研究微软的成功经验了!) 关于计算机技术的学习我想是这样的:学校开设的任何一门科学都有其滞后性,不要总认为自己掌握的某门技术就已经是天下无敌手了,虽然现在 Java,VB,C,C++用的都很多,怎能保证没有被淘汰的一天,我想.NET平台的诞生和X#语言的初见端倪完全可以说明问题。换言之,在我们掌握一门新技术的同时就又有更新的技术产生,身为当代的大学生应当有紧跟科学发展的素质。举个例子,就像有些同学总说,我做网页设计就喜欢直接写html,不愿意用什么Frontpage,Dreamweaver。能用语言写网页固然很好,但有高效的手段你为什么不使呢?仅仅是为了显示自己的水平高, unique? 我看真正水平高的是能够以最快的速度接受新事物的人。高级程序设计语言的发展日新月异,今后的程序设计就像人们在说话一样,我想大家从xml中应是有所体会了。难道我们真就写个什么都要用汇编,以显示自己的水平高,真是这样倒不如直接用机器语言写算了。反过来说,想要以最快的速度接受并利用新技术关键还是在于你对计算机科学地把握程度。 计算机技术牵扯的内容更为广泛些,一项一项说恐怕没个一年半载也说不清。我只想提醒大家的还是那句话,技术与科学是不能分家的,学好了科学同时搞技术,这才是上上策。犹如英语,原先人们与老外交流必须要个翻译,现在满马路的人都会说英语。就连 21世纪英语演讲比赛的冠军都轮不到英语系的学生了。计算机也是一样的,我们必须面对的一个现实就是:计算机真就只是一个工具,如果不具备其它方面的素养,计算机系的学生虽然不能说找不到工作,不过总有一天当其他专业性人才掌握了计算机技术后将比我们出色许多。原因就在于计算机解决的大都是实际问题,实际问题的知识却是我们少有的。单一的计算机技术没有立足之地。 我想是时候指出:学习每一个课程之前,都要先搞清这一课程的学习目的。这一学科的应用领域。据我自身所了解到的同龄同学和低年级的同学的学习状况:他们之中很少有人知道学一个学科的学习目的,期末考试结束了也不知道学这科做什么用。这就失去了读计算机科学的意义。当然这与现存的教育思想不能说一点关系都没有。 总的来说,从教育角度来讲,国内高校的课程安排不是很合理,强调理论,又不愿意在理论上深入教育,无力接受新技术,想避开新技术又无法避得一干二净。我觉得关键问题就是国内的高校难于突破现状,条条框框限制着怎么求发展。我们虽然认识得到国外教育的优越性,但为什么迟迟不能采取行动?哪怕是去粗取精的取那么一点点。我们需要改变。从我们自身角度来讲,多数人4年下来既没有学习计算机科学的学术水平,也没有学习计算机技术的那种韧劲。在我刚上大一时,我的计算机科学入门导师,淮北煤炭师范学院王爱平教授曾经对我说过这样一番话:“当你选择了计算机这一门科学,就意味着你踏上了一条不归路,就意味着你一生都要为之奋斗……你的身后是悬崖,只有向前走,不能往后退。” 有些同学说按照这样学习学的东西太多,有的未见得有用,我想打个形象的比方:学校学出来的人都是一个球体,方方面面的知识都应具备。可是社会上需要球体的地方很少,反而需要的是砖和瓦,即精通某一行的人才。但是对于同等体积的物体,用球体来改造是最方便最省事的。学校的学生很多,为了能够使更多的学生来适应这个社会,学校也就不得以把所有的学生都打造成一个球体,然后让社会对这些学生进行再加工,成为真正能够有用的人才。即使你非常清楚自己的将来要干什么,并且非常下定决心要走自己的路,这一步你也必须走,世界是在不断变化的,你不能预料未来。想清楚,努力去干吧! 必须结束这篇“胡侃”了,再侃下去非我力所能及。其实计算机还有很多基础课都值得一侃。怎奈我造诣有限,不敢再让内行耻笑。最后声明:这些只针对本科阶段的学习。即使把这些全弄通了,前面的路还长,计算机科学需要我们为之奋斗......学习计算机科学需要韧性,更需要创新,需要激情。深刻学习理论知识,勇于接受新技术的挑战,这才是我们这一代人应具有的素质。最后送大家一句话“Wake up every day with a feeling of passion for the difference technology will make in people's life!”。
2005年11月10日
六年前,我刚热恋“面向对象”(Object-Oriented)时,一口气记住了近十个定义。六年后,我从几十万行程序中滚爬出来准备写点心得体会时,却无法解释什么是“面向对象”,就象说不清楚什么是数学那样。软件工程中的时髦术语“面向对象分析”和“面向对象设计”,通常是针对“需求分析”和“系统设计”环节的。“面向对象”有几大学派,就象如来佛、上帝和真主用各自的方式定义了这个世界,并留下一堆经书来解释这个世界。
有些学者建议这样找“对象”:分析一个句子的语法,找出名词和动词,名词就是对象,动词则是对象的方法(即函数)。
当年国民党的文人为了对抗毛泽东的《沁园春·雪》,特意请清朝遗老们写了一些对仗工整的诗,请蒋介石过目。老蒋看了气得大骂:“娘希匹,全都有一股棺材里腐尸的气味。”我看了几千页的软件工程资料,终于发现自己有些“弱智”,无法理解“面向对象”的理论,同时醒悟到“编程是硬道理。”
面向对象程序设计语言很多,如Smalltalk、Ada、Eiffel、Object Pascal、Visual Basic、C++等等。C++语言最讨人喜欢,因为它兼容C 语言,并且具备C 语言的性能。近几年,一种叫Java 的纯面向对象语言红极一时,不少人叫喊着要用Java 革C++的命。我认为Java 好比是C++的外甥,虽然不是直接遗传的,但也几分象样。外甥在舅舅身上玩耍时洒了一泡尿,俩人不该为此而争吵。
关于C++程序设计的书藉非常多,本章不讲C++的语法,只讲一些小小的编程道理。如果我能早几年明白这些小道理,就可以大大改善数十万行程序的质量了。
1. C++面向对象程序设计的重要概念
早期革命影片里有这样一个角色,他说:“我是党代表,我代表党,我就是党。”后来他给同志们带来了灾难。
会用C++的程序员一定懂得面向对象程序设计吗?
不会用C++的程序员一定不懂得面向对象程序设计吗?
两者都未必。就象坏蛋入党后未必能成为好人,好人不入党未必变成坏蛋那样。
我不怕触犯众怒地说句大话:“C++没有高手,C 语言才有高手。”在用C 和C++编程8年之后,我深深地遗憾自己不是C 语言的高手,更遗憾没有人点拨我如何进行面向对象程序设计。我和很多C++程序员一样,在享用到C++语法的好处时便以为自己已经明白了面向对象程序设计。就象挤掉牙膏卖牙膏皮那样,真是暴殄天物呀。
人们不懂拼音也会讲普通话,如果懂得拼音则会把普通话讲得更好。不懂面向对象程序设计也可以用C++编程,如果懂得面向对象程序设计则会把C++程序编得更好。本节讲述三个非常基础的概念:“类与对象”、“继承与组合”、“虚函数与多态”。理解这些概念,有助于提高程序的质量,特别是提高“可复用性”与“可扩充性”。
1.1 类与对象
对象(Object)是类(Class)的一个实例(Instance)。如果将对象比作房子,那么类就是房子的设计图纸。所以面向对象程序设计的重点是类的设计,而不是对象的设计。类可以将数据和函数封装在一起,其中函数表示了类的行为(或称服务)。类提供关键字public、protected 和private 用于声明哪些数据和函数是公有的、受保护的或者是私有的。
这样可以达到信息隐藏的目的,即让类仅仅公开必须要让外界知道的内容,而隐藏其它一切内容。我们不可以滥用类的封装功能,不要把它当成火锅,什么东西都往里扔。
类的设计是以数据为中心,还是以行为为中心?
主张“以数据为中心”的那一派人关注类的内部数据结构,他们习惯上将private 类型的数据写在前面,而将public 类型的函数写在后面,如表8.1(a)所示。
主张“以行为为中心”的那一派人关注类应该提供什么样的服务和接口,他们习惯上将public 类型的函数写在前面,而将private 类型的数据写在后面,如表8.1(b)所示。
很多C++教课书主张在设计类时“以数据为中心”。我坚持并且建议读者在设计类时“以行为为中心”,即首先考虑类应该提供什么样的函数。Microsoft 公司的COM 规范的核心是接口设计,COM 的接口就相当于类的公有函数[Rogerson 1999]。在程序设计方面,咱们不要怀疑Microsoft 公司的风格。
设计孤立的类是比较容易的,难的是正确设计基类及其派生类。因为有些程序员搞不清楚“继承”(Inheritance)、“组合”(Composition)、“多态”( Polymorphism)这些概念。
1.2 继承与组合
如果A 是基类,B 是A 的派生类,那么B 将继承A 的数据和函数。示例程序如下:
class A
{
public:
void Func1(void);
void Func2(void);
};
class B : public A
{
public:
void Func3(void);
void Func4(void);
};
// Example
main()
{
B b; // B的一个对象
b.Func1(); // B 从A 继承了函数Func1
b.Func2(); // B 从A 继承了函数Func2
b.Func3();
b.Func4();
}
这个简单的示例程序说明了一个事实:C++的“继承”特性可以提高程序的可复用性。正因为“继承”太有用、太容易用,才要防止乱用“继承”。我们要给“继承”立一些使用规则:
一、如果类A 和类B 毫不相关,不可以为了使B 的功能更多些而让B 继承A 的功能。
不要觉得“不吃白不吃”,让一个好端端的健壮青年无缘无故地吃人参补身体。
二、如果类B 有必要使用A 的功能,则要分两种情况考虑:
(1)若在逻辑上B 是A 的“一种”(a kind of ),则允许B 继承A 的功能。如男人(Man)是人(Human)的一种,男孩(Boy)是男人的一种。那么类Man 可以从类Human 派生,类Boy 可以从类Man 派生。示例程序如下:
class Human
{
…
};
class Man : public Human
{
…
};
class Boy : public Man
{
…
};
(2)若在逻辑上A 是B 的“一部分”(a part of),则不允许B 继承A 的功能,而是要用A和其它东西组合出B。例如眼(Eye)、鼻(Nose)、口(Mouth)、耳(Ear)是头(Head)的一部分,所以类Head 应该由类Eye、Nose、Mouth、Ear 组合而成,不是派生而成。示例程序如下:
class Eye
{
public:
void Look(void);
};
class Nose
{
public:
void Smell(void);
};
class Mouth
{
public:
void Eat(void);
};
class Ear
{
public:
void Listen(void);
};
// 正确的设计,冗长的程序
class Head
{
public:
void Look(void) { m_eye.Look(); }
void Smell(void) { m_nose.Smell(); }
void Eat(void) { m_mouth.Eat(); }
void Listen(void) { m_ear.Listen(); }
private:
Eye m_eye;
Nose m_nose;
Mouth m_mouth;
Ear m_ear;
};
如果允许Head 从Eye、Nose、Mouth、Ear 派生而成,那么Head 将自动具有Look、Smell、Eat、Listen 这些功能:
// 错误的设计
class Head : public Eye, public Nose, public Mouth, public Ear
{
};
上述程序十分简短并且运行正确,但是这种设计却是错误的。很多程序员经不起“继承”的诱惑而犯下设计错误。
一只公鸡使劲地追打一只刚下了蛋的母鸡,你知道为什么吗?
因为母鸡下了鸭蛋。
本书3.3 节讲过“运行正确”的程序不见得就是高质量的程序,此处就是一个例证。
1.3 虚函数与多态
除了继承外,C++的另一个优良特性是支持多态,即允许将派生类的对象当作基类的对象使用。如果A 是基类,B 和C 是A 的派生类,多态函数Test 的参数是A 的 指针。那么Test 函数可以引用A、B、C 的对象。示例程序如下:
class A
{
public:
void Func1(void);
};
void Test(A *a)
{
a->Func1();
}
class B : public A
{
…
};
class C : public A
{
…
};
// Example
main()
{
A a;
B b;
C c;
Test(&a);
Test(&b);
Test(&c);
};
以上程序看不出“多态”有什么价值,加上虚函数和抽象基类后,“多态”的威力就显示出来了。
C++用关键字virtual 来声明一个函数为虚函数,派生类的虚函数将(override)基类对应的虚函数的功能。示例程序如下:
class A
{
public:
virtual void Func1(void){ cout<< “This is A::Func1
”}
};
void Test(A *a)
{
a->Func1();
}
class B : public A
{
public:
virtual void Func1(void){ cout<< “This is B::Func1
”}
};
class C : public A
{
public:
virtual void Func1(void){ cout<< “This is C::Func1
”}
};
// Example
main()
{
A a;
B b;
C c;
Test(&a); // 输出This is A::Func1
Test(&b); // 输出This is B::Func1
Test(&c); // 输出This is C::Func1
};
如果基类A 定义如下:
class A
{
public:
virtual void Func1(void)=0;
};
那么函数Func1 叫作纯虚函数,含有纯虚函数的类叫作抽象基类。抽象基类只管定义纯虚函数的形式,具体的功能由派生类实现。
结合“抽象基类”和“多态”有如下突出优点:
(1)应用程序不必为每一个派生类编写功能调用,只需要对抽象基类进行处理即可。这一
招叫“以不变应万变”,可以大大提高程序的可复用性(这是接口设计的复用,而不是代码实现的复用)。
(2)派生类的功能可以被基类指针引用,这叫向后兼容,可以提高程序的可扩充性和可维护性。以前写的程序可以被将来写的程序调用不足为奇,但是将来写的程序可以被以前写的程序调用那可了不起。
2 良好的编程风格
内功深厚的武林高手出招往往平淡无奇。同理,编程高手也不会用奇门怪招写程序。良好的编程风格是产生高质量程序的前提。
2.1 命名约定
有不少人编程时用拼音给函数或变量命名,这样做并不能说明你很爱国,却会让用此程序的人迷糊(很多南方人不懂拼音,我就不懂)。程序中的英文一般不会太复杂,用词要力求准确。匈牙利命名法是Microsoft 公司倡导的[Maguire 1993],虽然很烦琐,但用习惯了也就成了自然。没有人强迫你采用何种命名法,但有一点应该做到:自己的程序命名必须一致。
以下是我编程时采用的命名约定:
(1)宏定义用大写字母加下划线表示,如MAX_LENGTH;
(2)函数用大写字母开头的单词组合而成,如SetName, GetName ;
(3)指针变量加前缀p,如*pNode ;
(4)BOOL 变量加前缀b,如bFlag ;
(5)int 变量加前缀i,如iWidth ;
(6)float 变量加前缀f,如fWidth ;
(7)double 变量加前缀d,如dWidth ;
(8)字符串变量加前缀str,如strName ;
(9)枚举变量加前缀e,如eDrawMode ;
(10)类的成员变量加前缀m_,如m_strName, m_iWidth ;
对于int, float, double 型的变量,如果变量名的含义十分明显,则不加前缀,避免烦琐。如用于循环的int 型变量i,j,k ;float 型的三维坐标(x,y,z)等。
2.2 使用断言
程序一般分为Debug 版本和Release 版本,Debug 版本用于内部调试,Release 版本发行给用户使用。断言assert 是仅在Debug 版本起作用的宏,它用于检查“不应该”发生的情况。以下是一个内存复制程序,在运行过程中,如果assert 的参数为假,那么程序就会中止(一般地还会出现提示对话,说明在什么地方引发了assert)。
//复制不重叠的内存块
void memcpy(void *pvTo, void *pvFrom, size_t size)
{
void *pbTo = (byte *) pvTo;
void *pbFrom = (byte *) pvFrom;
assert( pvTo != NULL && pvFrom != NULL );
while(size - - > 0 )
*pbTo + + = *pbFrom + +
return (pvTo);
}
assert 不是一个仓促拼凑起来的宏,为了不在程序的Debug 版本和Release 版本引起差别,assert 不应该产生任何副作用。所以assert 不是函数,而是宏。程序员可以把assert 看成一个在任何系统状态下都可以安全使用的无害测试手段。
很少有比跟踪到程序的断言,却不知道该断言的作用更让人沮丧的事了。你化了很多时间,不是为了排除错误,而只是为了弄清楚这个错误到底是什么。有的时候,程序员偶尔还会设计出有错误的断言。所以如果搞不清楚断言检查的是什么,就很难判断错误是出现在程序中,还是出现在断言中。幸运的是这个问题很好解决,只要加上清晰的注释即可。这本是显而易见的事情,可是很少有程序员这样做。这好比一个人在森林里,看到树上钉着一块“危险”的大牌子。但危险到底是什么?树要倒?有废井?有野兽?除非告诉人们“危险”是什么,否则这个警告牌难以起到积极有效的作用。难以理解的断言常常被程序员忽略,甚至被删除。[Maguire 1993]
以下是使用断言的几个原则:
(1)使用断言捕捉不应该发生的非法情况。不要混淆非法情况与错误情况之间的区别,后者是必然存在的并且是一定要作出处理的。
(2)使用断言对函数的参数进行确认。
(3)在编写函数时,要进行反复的考查,并且自问:“我打算做哪些假定?”一旦确定了的
假定,就要使用断言对假定进行检查。
(4)一般教科书都鼓励程序员们进行防错性的程序设计,但要记住这种编程风格会隐瞒错误。当进行防错性编程时,如果“不可能发生”的事情的确发生了,则要使用断言进行报警。
2.3 new、delete 与指针
在C++中,操作符new 用于申请内存,操作符delete 用于释放内存。在C 语言中,函数malloc 用于申请内存,函数free 用于释放内 存。由于C++兼容C 语言,所以new、delete、malloc、free 都有可能一起使用。new 能比malloc 干更多的事,它可以申请对象的内存,而malloc 不能。C++和C 语言中的指针威猛无比,用错了会带来灾难。对于一个指针p,如果是用new申请的内存,则必须用delete 而不能用free 来释放。如果是用malloc 申请的内存,则必须用free 而不能用delete 来释放。在用delete 或用free 释放p 所指的内存后,应该马上显式地将p 置为NULL,以防下次使用p 时发生错误。示例程序如下:
void Test(void)
{
float *p;
p = new float[100];
if(p==NULL) return;
…// do something
delete p;
p=NULL; // 良好的编程风格
// 可以继续使用p
p = new float[500];
if(p==NULL) return;
…// do something else
delete p;
p=NULL;
}
我们还要预防“野指针”,“野指针”是指向“垃圾”内存的指针,主要成因有两种:
(1)指针没有初始化。
(2)指针指向已经释放的内存,这种情况最让人防不胜防,示例程序如下:
class A
{
public:
void Func(void){…}
};
void Test(void)
{
A *p;
{
A a;
p = &a; // 注意a 的生命期
}
p->Func(); // p 是“野指针”,程序出错
}
2.4 使用const
在定义一个常量时,const 比#define 更加灵活。用const 定义的常量含有数据类型,该常量可以参与逻辑运算。例如:
const int LENGTH = 100; // LENGTH 是int 类型
const float MAX=100; // MAX 是float 类型
#define LENGTH 100 // LENGTH 无类型
#define MAX 100 // MAX 无类型
除了能定义常量外,const 还有两个“保护”功能:
一、强制保护函数的参数值不发生变化
以下程序中,函数f 不会改变输入参数name 的值,但是函数g 和h 都有可能改变name的值。
void f(String s); // pass by value
void g(String &s); // pass by referance
void h(String *s); // pass by pointer
main()
{
String name=“Dog”;
f(name); // name 的值不会改变
g(name); // name 的值可能改变
h(name); // name 的值可能改变
}
对于一个函数而言,如果其‘&’或‘*’类型的参数只作输入用,不作输出用,那么应当在该参数前加上const,以确保函数的代码不会改变该参数的值(如果改变了该参数的值,编译器会出现错误警告)。因此上述程序中的函数g 和h 应该定义成:
void g(const String &s);
void h(const String *s);
二、强制保护类的成员函数不改变任何数据成员的值
以下程序中,类stack 的成员函数Count 仅用于计数,为了确保Count 不改变类中的任何数据成员的值,应将函数Count 定义成const 类型。
class Stack
{
public:
void push(int elem);
void pop(void);
int Count(void) const; // const 类型的函数
private:
int num;
int data[100];
};
int Stack::Count(void) const
{
++ num; // 编译错误,num 值发生变化
pop(); // 编译错误,pop 将改变成员变量的值
return num;
}
2.5 其它建议
(1)不要编写一条过分复杂的语句,紧凑的C++/C 代码并不见到能得到高效率的机器代码,却会降低程序的可理解性,程序出错误的几率也会提高。
(2)不要编写集多种功能于一身的函数,在函数的返回值中,不要将正常值和错误标志混在一起。
(3)不要将BOOL 值TRUE 和FALSE 对应于1 和0 进行编程。大多数编程语言将FALSE定义为0,任何非0 值都是TRUE。Visual C++将TRUE 定义为1,而Visual Basic 则将TRUE定义为-1。示例程序如下:
BOOL flag;
…
if(flag) { // do something } // 正确的用法
if(flag==TRUE) { // do something } // 危险的用法
if(flag==1) { // do something } // 危险的用法
if(!flag) { // do something } // 正确的用法
if(flag==FALSE) { // do something } // 不合理的用法
if(flag==0) { // do something } // 不合理的用法
(4)小心不要将“= =”写成“=”,编译器不会自动发现这种错误。
(5)不要将123 写成0123,后者是八进制的数值。
(6)将自己经常犯的编程错误记录下来,制成表格贴在计算机旁边。
3 小结
C++/C 程序设计如同少林寺的武功一样博大精深,我练了8 年,大概只学到二三成。所以无论什么时候,都不要觉得自己的编程水平天下第一,看到别人好的技术和风格,要虚心学习。本章的内容少得可怜,就象口渴时只给你一颗杨梅吃,你一定不过瘾。我借花献佛,推荐一本好书:Marshall P. Cline 著的《C++ FAQs》[Cline 1995]。你看了后一定会赞不绝口。会编写C++/C 程序,不要因此得意洋洋,这只是程序员基本的技能要求而已。如果把系统分析和系统设计比作“战略决策”,那么编程充其量只是“战术”。如果指挥官是个大笨蛋,士兵再勇敢也会吃败仗。所以我们程序员不要只把眼光盯在程序上,要让自己博学多才。我们应该向北京胡同里的小孩们学习,他们小小年纪就能指点江山,评论世界大事。
2005年11月8日
Bresenham 画线算法
Bresenham 画线算法是由Bresenham提出的一种精确而有效的光栅线生成算法,该算法仅仅使用了
整数的增量来实现的。
Bresenham算法将对整形参数的符号检测,整形参数的值比于实际险段之间的偏量。
Bresenham算法内容
对于直线方程 Y = kX + b;
A,0< 斜率 < 1 时候的算法
1,输入线段的两个端点Point1和Point2,并且存储到Point1(x1,y1)和Point2(x2,y2)中;
2,将Point1载入桢缓存,绘画第一个起始点;
3,计算常量△x、△y、2△y和2(△y-△x),并且获得一个决策参数的第一个值:P = 2△y - △x;
4,从n = 0开始,在沿线经过每个Xn处,进行下面的监测:
如果Pn< 0, 下一个点绘制的是(Xn+1,yn),并且Pn+1= Pn + 2△y
如果P0>= 0 ,下一个点绘制的是(Xn+1,Yn+1),并且Pn+1 = Pn + 2(△y-△x)
5,重复执行△x-1次步骤4;
关于在C++中的算法如下实例:
#include
using namespace std;
void Bresenham(int x1,int y1,int x2,int y2){
int dx = x2 - x1;//△x
int dy = y2 - y1;//△y
int p = (2*dy) - dx ; //P = 2△y - △x
int dobDy = 2* dy ; // 2 △y
int dobD = 2*(dy - dx) ; // 2(△y - △x)
int PointX,PointY;
//设置两个临时用来显示位置的变量
if( x1 > x2){ //判断线段的方向
PointX = x2;//起始坐标X
PointY = y2;////起始坐标Y
x2 = x1;
}
else{
PointX = x1;//起始坐标X
PointY = y1;//起始坐标Y
}
//达因第一个起始点
cout<<"Point: X:"<>
2005年11月7日
1、大学生活丰富多彩,会令你一生都难忘,但难忘有很多种,你可以学了很多东西而难忘,也会因为什么都没学到而难忘!
2、计算机专业是一个很枯燥的专业,但即来之、则安之,只要你努力学,也会发现其中的乐趣的。
3、记住:万丈高楼平地起!基础很重要,尤其是专业基础课,只有打好基础才能学得更深。
4、C语言是基础,很重要,如果你不学好C语言,那么什么高级语言你都学不好。
5、C语言与C++语言是两回事。就象大熊猫和小熊猫一样,只是名字很像!
6、请先学习专业课《数据结构》、《计算机组成原理》,不要刚开始就拿着一本VC在看,你连面向对象都搞不清楚,看VC没有任何用处。
7、对编程有一定的认识后,就可以学习C++了。(是C++而不是VC,这两个也是两码事!C++是一门语言,而VC教程则是讲解如何使用MFC类库,学习VC应建立在充分了解C++的基础之上。看VC的书,是学不了C++语言的。)
8、学习编程的秘诀是:编程,编程,再编程;
9、认真学习每一门专业课,那是你的吃饭碗。
10、在学校的实验室就算你做错一万次程序都不会有人骂你,如果你在公司你试试看!所以多去实验室上机,现在错的多了,毕业后就错的少了。
11、从现在开始,在写程序时就要养成良好的习惯。
12、不要漏掉书中任何一个练习题--请全部做完并记录下解题思路。
13、你会买好多参考书,那么请把书上的程序例子亲手输入到电脑上实践,即使配套光盘中有源代码。
14、VC、C#、.NET这些东西都会过时,不会过时的是数据结构和优秀的算法!
15、记住:书到用时方恨少。不要让这种事发生在你身上,在学校你有充足的时间和条件读书,多读书,如果有条件多读原版书,你要知道,当一个翻译者翻译一本书时,他会不知不觉把他的理念写进书中,那本书就会变得像鸡肋!
16、我还是强调认真听专业课,因为有些课像《数据结构》、《编译原理》、《操作系统》等等,这种课老师讲一分钟能让你明白的内容,你自己看要看好几个月,有的甚至看了好几年都看不明白。
17、抓住在学校里的各种实践的机会,要为自己积累经验,就业时经验比什么都有用。
18、多去图书馆,每个学校的图书馆都有很多好书等你去看!
19、编程不是技术活,而是体力活。
20、如果你决定了要当一个好的程序员,那么请你放弃游戏,除非你是那种每天只要玩游戏就能写出好程序的天才!
21、你要有足够的韧性和毅力!有个高手出了一道题测试你的韧性和毅力:找个10000以内的素数表,把它们全都抄下来,然后再检查三遍,如果能够不间断地完成这一工作,你就可以满足这一条。
22、找到只属于你自己的学习方法!不要盲目的追随别人的方法,适合自己的才是最好的!
2005年11月5日
迷时师度 悟了自度
侯捷 1998.04.20 第一次发表于清大.枫挢驿站.电脑书讯 CompBook 版(140.114.87.5)
元智大学和清华大学的资讯系同学,最近不约而同邀请我在自校的资讯周上为同学们做个演讲。对于那些吊尔郎当,连自己也不关心自己的同学,我向来一点气力都舍不得花;但对于积极上进的同学,我可倒是很喜欢和他们谈话。曾经在不同的学校讲过好些个不同的谈话性题目(非关技术,但都不脱资讯范围),这次有感于在学校任教三年了,颇知道同学们的一些学习情况,所以想和大家谈谈学习的心态、老师的角色、以及学习者自己的角色。
我想先说一个小故事。
●六祖檀经
佛教东传中土之后,虽然大德大贤辈出,但由中国和尚所著,被奉为「经」者,也只「六祖檀经」一书而已。
六祖檀经记录着佛教禅宗六祖惠能一生的思想言行。其中对于六祖得道的过程,有详细的叙述。 六祖惠能俗姓卢,南海新兴人。年少丧父,母且寡居,家亦屡空,业无腴产。所以他从小做了樵夫,养家活口。有一天他偶闻肆间诵金刚般若经,心有所感,於是询问邻人何处学佛,遂安置母亲,至河北黄梅山礼拜五祖。
●五祖弘忍
五祖弘忍见惠能谈吐甚有根性,不动声色地让他到後院碓米。八个月後有一天,弘忍告诉众弟子说,每一个人写一首偈,谁能悟道,我便传他衣钵。当时人人以为五祖座下第一大弟子神秀必得衣钵,所以也没有人去作这首偈。神秀在这种情况下,一来「责无旁贷」,二来也想让师父看看自己心中的见解,便在三更时候於南廊壁上写下心偈一首:
身是菩提树,心如明镜台;时时勤拂拭,莫使惹尘埃。
第二天五祖弘忍见到了这首偈,把神秀叫到座前询问。并谓:「汝作此偈,见即未到,只到门前,尚未得入...」。要神秀回去再作一偈。过了数天,神秀却作不出来。
●千古名诵
有个童子行过碓房,唱诵神秀的偈。惠能闻後,问明缘由,便请童子引至大堂。礼拜之後,惠能说自己也有一首偈,但是他既不识字更不会写字,乃请人代书写下千古名诵:
菩提本无树,明镜亦非台;本来无一物,何处拈尘埃。
此偈一成,众总皆惊。消息传至五祖,了然於心,但恐人损害,遂脱了鞋子当抹布,立刻把这首偈擦掉,并且说「这首也嘛没有悟道」!
第二天,五祖弘忍悄悄来到後院,看到惠能把大石头系在腰上,增加身体的重量来碓米,很是感动,於是和惠能展开一段非常空灵神妙的对话。弘忍问「米熟也未」?惠能曰:「米熟久矣,犹欠筛在」。於是祖以仗击碓三下而去。惠能知祖意,是夜三鼓入室,由祖为说金刚经。当说到「因无所住而生其心」,惠能大悟,於是五祖授衣钵以为信,并且说「衣为争端,止汝勿传...,汝须速去,恐人害汝」。
●迷时师度,悟了自度
五祖弘忍亲自送惠能到九江驿,令上船,把 自摇。惠能曰:「请和尚坐,弟子合摇 」。祖云:「合是吾渡汝」。惠能云:「迷时师度,悟了自度」。
我曾经在研究所毕业的前夕,因为看了这个故事,有感师恩,在实验室黑板上写下大大的这八个字,表达我心中强烈的情绪。我知道我就要永远告别我的学生生涯,再也没有人度我,我要自己度化自己了。
今天我意料之外地成为许多人的老师。远在接受学校的聘书之前,我其实也已经在外面开过很多课程,做着授业解惑的神圣工作。我的书籍文章,更使我扮演与广大读者之间一种默默的师生关系。每当一个课程的开始,不管面对的是业界的工程师,或是学校的学子,我总是告诉他们,课程是短暂的,课後才是长久的。一个课程就算长达一学期,也只是(只能是)奠定学习者日後自修的基础。
进入社会的人,我想比较能够了解这里面的意义。象牙塔里的学生,即令闻言成理,总似乎不容易感受深刻。於是日复一日,年复一年,看着同学们勤嬉戏而荒精业。我带过好几届学生,大凡转学生、五专插班生、重考生都比较用功些,大概因为他们经历过一些小小的挫折。而他们的努力,很快使他们立於同侪之上。
挫折在人生早些时候到来,谁曰不宜!
●谁悟了?
禅宗讲究顿悟,基本上是一种比较「玄」的境界。说是「迷时师度,悟了自度」,其实,除了大德大贤,谁又敢说自己悟了?智慧容或可以顿悟,知识又岂能够如此?谁敢说他对知识「突然都懂了」?所以「悟了自度」的「悟」,在这里我把它解释为「有了扎实的基础」:当你有了足够的基础,你就可以不依靠任何人任何时间任何地点传授您知识。只要有书籍,你随时可以自度。拥有足够的基础,你甚至可以开始另一种更高层次的自度:你开始自己设想问题,自己推敲解答,自己设计验证的方法,并自己判断逻辑有没有破绽。再高明的老师,也没有办法把自己的悟力灌入学生心中;唯有学生自己动念思考,再由老师协助解开关键环节,学生自己才能开悟。
●南宗与北宗,顿悟与渐悟
佛法有顿悟,学问可没有。如果有人说,我突然在某一天对 Java 开悟了,对 OO 开悟了,对 MFC 开悟了...,我想那是他刻意(为了炫耀)或非刻意(因为遗忘)地忽略了他所谓的「悟」那一天之前的所有努力。是的,那叫渐悟,不是顿悟。
Inside OLE 一书作者 Kraig Brockschmidt 在他的序里面有这段话:
1993 年一月的某个周日下午,当我正做着与 OLE 全然无关的事情时,我突然获得了所谓的 OLE 涅 状态。所有关於 OLE 的支支节节突然全都归定位。在六个月的模糊心智之後,我突然看清楚了 OLE。
Essential COM 一书作者 Don Box 在他的序里面亦有一段类似的话:
幸运的是有一天(1998 年八月八日),突然像神迹一般,COM 对我变得再明白不过,我终於了解了 COM 的动机。如何把这个 programming model 应用在每天遇到的程式设计问题中,也因此显得再明白不过。
听起来都是顿悟的例子。难道学习 COM/OLE 特别需要宗教信仰吗?我想是因为这些技术特别需要高度抽象思考,使得豁然开朗後的喜悦巨大到令人觉得是一种「突然的神迹降临」。其实你我都明白不过,知识点的突破,是靠知识面的累积。
●时时勤拂拭,莫使惹尘埃
既然知道点的突破要靠面的累积,同时也知道我们「具足凡夫」,没有大宗师的智慧,我建议你还是多体会神秀的见解(六祖佛法南传,神秀创立北宗,主张渐悟)。把神秀的「时时勤拂拭,莫使惹尘埃」至少应用在你满桌子的经典好书上面。噢,你应该有满桌子的好书吧?没有?!那我就很为你惋息了,你连自度的小船都没有,连自救的工具都没有,谁来拯救你?我知道好多工程师桌上可怜没几本书,月入倒有 7,8 万,年终股票分红的张数比全年买书的本数还多。叹!不看书,你的日子会愈来愈贫乏,愈来愈空虚,因为你一直掏东西出去,掏空了人也就瘪掉了。
工程师或许说「我都嘛看 paper 和 document。看书?呵呵,等书出来都太慢了啦」。喔,在 project 方面你不得不冲,你不冲公司也会要你冲。但你不觉得有很多基本的东西,其实你摇摇晃晃?有很多基础的东西,其实你从来没有好好把它搞懂变成自己的中心思想?你的 project 总是急如星火,所以可以 work 就好啦!「旗正飘飘(到处埋设 flag)」管它的!模组规划管它的!文件撰写管它的!目光浅短者,从来不去想日後的维护工作要如何进行,「深谋远虑」者,则想反正到时候我已经走人了。
这不就是我所看到的业界通病吗?
「学海无涯,唯勤是岸」,这句话我总想错不了。有人说,勤勉二字诀不是万灵丹,若无兴趣,再勤勉也没有用。我想从两个角度来看这件事。第一,「勤」能不能够克服「难」?如果目标是放在成为一个胜任的从业人员,这一点应该没有问题,毕竟 computer science 能有多难?Computer engineering 又能有多难?多花点功夫还有读不会的吗!一本不懂,再读另一本(前提是都是筛选过的好书)。一试再试做不成,再试一下嘛。 第二,「勤」能不能够克服「没兴趣」?大概不能!「学海无涯,唯勤是岸」这句话本来就是要以兴趣做为基础,这是大前提,着毋庸议,本来就不必说的。电子、电机、资讯的循环太快,竞争太烈,如果没有强烈的兴趣,念起来会非常非常痛苦,趁早转系转业吧。
然而,有多少人根本是懵懵懂懂地进入大学,懵懵懂懂地填了科系?他们根本不知道自己真正的兴趣在哪里。也许兴趣会因为努力後的成绩提升而加分,亦未可知呀! 坚定知道自己兴趣在哪里的人,是最幸福的人。自从我自己的人生终於归定位後,我清楚地有这种感受。 元智大学管理学院从去年起就大一不分系,大二开始才由同学自己选系。我非常佩服校方的这种从善精神与兴革魄力。
●书籍是永远的老师
说到底,阅读习惯以及鉴赏能力的培养,是你能不能够在求学的道路上「自度」的关键。广义地说,也是你能不能够在人生道路上「自度」的关键。如果一辈子要靠别人来指点你该看哪些书,该怎麽看,忒也太没有长进了。你能阅读,就能鉴赏;阅读数量愈多,鉴赏能力愈高。很多人好奇我如何有能力评析那麽多又厚又深的大块头书,无它,因为我读的多,因为我读的深。
【余秋雨.台湾演讲】(余秋雨/尔雅/1998)一书中在「建议阅读」这篇文章上写道,「我觉得一个人的最佳读书状态大多产生在中年以後,但能不能取得这种状态则取决於青年时期的准备」。余先生又说,曾有两位年老的大学图书馆管理员告诉他,他们能从一年级学生的借书卡上预测这些学生将来的成就,几乎百试不爽。有人毫无规律胡乱借,有人穷数年之力死啃一大堆名着,有人的借书卡过於疏空,有人的借书卡却又太密密麻麻。
希望这些话能对我们产生一些启示。对於尚在学校就读的学生,我想特别指出,进入社会之後的工作是如此繁忙,你更需要在学校把握机会,奠定良好的习惯和良好的基础。
●边际效益
如果你有一笔钱,放在银行的活存利息有 3%,定存利息有 7%,而且你暂时无需用钱,那麽你会把钱活存还是定存?
当然是定存。多麽简单的选择。
如果你知道,课前预习的话,上课听讲可吸收 70%;课後立刻复习,可把效果再提升 10%;课後第二天复习,只能提升 5%;课後第三天才复习,能保住原来的 70% 不错了;课後一个星期复习,只能保持 30%;期末考前才看,则当时脑袋瓜里只留有 10% 的残存记忆。
那麽,你会课前预习并且课後立刻复习吗?
很多人在处理金钱时都是聪明人,在处理时间时都愚蠢的要命。
你是哪一种?
●如果可以循序来
一位读者写 email 给我,他说『有点可惜,Windows 3.0年代我刚开始碰电脑。如果照程序走的话,应该可以把基础打好的』。
面对此言我思潮起伏,想起自己这五、六年来的人生转折。『如果照程序走的话』,是的,我很幸运地照了程序来走,所以路不是太颠簸。我从 DOS 时代开始学习C programming、然後触摸低阶的 BIOS call 和 interrupt并学习 assembly 语言,然後研究保护模式以及记忆体管理,然後学习 SDK programming、学习 C++、学习 MFC programming、钻研 Windows 作业系统...。由於基础愈挖愈深,成果愈垒愈高,新技术新知识也就愈来愈容易上手。核心基础打得稳,更能够以不变应万变。
现在才面对这五彩缤纷花花亮丽的资讯世界的人,不管你是大一新生,或是忙於工作专案一直没有机会充电,还停留在古早年代的工程师,我可以想像你们的苦闷。新知识新技术如泰山压顶,你不知道从哪一条路开始走。有师长前辈指导正确的学习次序已是万幸,有些东西(还不少)偏又不是那麽有绝对的次序性。好比说作业系统好了,我是先学习抽象的一般性作业系统概论(例如大三安排的课程)呢?还是先学习专一而具象的作业系统(例如 Windows)实作技术呢?一开始就抽象会使我坠入五里路,没有踏实感;一开始就具象又使我的常识与一般观念不足。又例如C++ 和 OO,臻於高峰前通常必须经过几个阶段的反覆学习,包括语言本身、程式实务经验、classes 设计能力、市售application framework 之运用、对 object model 的彻底认识。这些阶段并不是结束一个而後开始另一个,它们都有重叠性。经过这样的铺陈,你会发现,原来通往 COM的基础已经在这些阶段中扎好了。
身在知识爆炸的时代,算是对求知行为的一个不幸吧。可是,永远有人生在你之前,也永远有人生於你之後,还能怎麽样?认命一点。
●令人佩服的学习精神
这学期我开的 Win32 system programming and Win95 OS课程,除了自校学生选修,就我所知道的还有交大两位大学生和清大一位研究生,以及中坜地区几位工程师来旁听。过去也曾有台北来的工程师旁听。我非常佩服这些同学的学习精神。从台北或新竹坐车到内坜可不是件轻松的事,尤其在那想起来就头痛欲裂的下班时段。有一天我顺巧载那位清大研究生回竹,才知道他每周四上完我的课回到住处都超过晚上 11 点了。
为了上三小时的课,前後再花四小时。这样的求知心真是不容易呀,我很佩服。 旁听的同学永远是最认真的,远道来的同学又更认真。因为他们更懂得珍惜。
●经典好书再次整理
三不五时就会收到一些 email,要我推荐专业上的书籍。我再次把个人研习领域(C,C++,Win32 OS,Win32 Programming)内的一些好书胪列出来给大家做叁考。
很多人混淆了所谓的入门书与基础书。入门书必须浅显易懂,因为它面对的是全然无知的新手;如何吸引并提升读者的兴趣,几乎是入门书最重要的功课。基础书不一样,它的读者早已入门,甚至有相当经验;它的任务是为读者挖好更深更实在的基础。通常这种书的论述层次都很深,一件事直接探到了源头;碰触范围也很广,旁徵博引以求全效。
通常过了入门阶段,一本入门书对你已是毫无滋味(但不要回过头来骂它,毕竟你也曾受过它的帮助。何况一本好的入门书是非常不容易写的)。至於基础书,可以伴你长长久久,上面的注解眉批、心得笔记,甚至还可以唤起你的许多艰苦而欣慰的回忆。
下面全都是可以为你奠基的书。其中 AW 是 Addison Wesley的缩写,MP 是 Microsoft Press 的缩写。如果有中译本,我会在书名後面加注,但这并不代表推荐。这些经典的中译品质,有的很好,有的普通,也有的错误百出。有些书我不确定有没有中译本,各位网友如果知道,请告诉我或公开回覆,谢谢。除了 "Macro Assembler Bible" 年代久远,其他都是 1996 以後的新书。
⊙Assemble
1. Microsoft Macro Assembler Bible 2/e(Barkakati/SAMS) 峰中译
⊙C++:
1. C++ Primer 3/e(Lippman/AW) 原文书即将出版。旧版有无中译?
2. The C++ Programming Language 3/e(Stroustrup/AW) 有无中译?儒林?
3. Inside The C++ Object Model(Lippman/AW) 棋峰将有中译本
⊙SDK:
1. Programming Windows 95(Petzold/MP) 松格中译
//注:大陆版-清华大学出版社
2. Windows 95 : A Developer's Guide(Richter/M&T Books) 棋峰中译
//注:大陆版-清华大学出版社
⊙VxD:
1. System Programming for Windows 95(Oney/MP)松岗将有中译
2. Writing Windows VxDs and Device Deviers 2/e(Hazzah/R&D Books)无中译本
⊙Windows O.S(and/or Windows System Programming)
1. Windows 95 System Programming Secrets(Matt/IDG Books)旗标中译
//注:大陆版-清华大学出版社
2. Advanced Windows 3/e(Richter/Microsoft Press)松岗中译
⊙Windows NT kernel mode driver:
1. The Windows NT Device Driver Book(Baker/Prentice Hall) 无中译本
⊙MFC
1. Inside Visual C++ 4/e(Kruglinski/MP) 无中译本。旧版由 棋峰中译
//注:大陆版4版-清华大学出版社
// 大陆版5版-希望出版社
2. Programming Windows 95 with MFC(Prosise/MP) 松格中译
3. 深入浅出 MFC 2/e(侯俊杰/松岗) 中文书
//注:大陆版--华中理工出版社
4. MFC Internals(Shepherd & Wingo/AW) 无中译本
⊙COM
1. Essential COM(Don Box/Addison Wesley) 棋峰将有中译
//注:有大陆版
2005年11月1日
要提高写程序的能力只能通过多练习。多写和调试一些程序,从简单的做起,多读一些例程。C++是C的延伸,我觉得C是基础。用C能够熟练地写程序以后,再逐渐接触C++的程序设计理念可能更好一些。
人类的语言是工具,计算机语言也是工具.掌握工具的最好的办法是做个(稍微大点的)东西,这样对语言会有更好的理解,知道它能做什么不能作什么,然后才是做什么工程选什么工具.刚开始学语言的时候常常背语法,其实更重要的是练习练习再(大点的)练习.
"关于学C++, 我向你推荐一些书(当然能够结合课内项目实践更好)
1。The C++ Programming Language(Bjarne Stroustrup)
2。 Inside The C++ Object Model (Stanley B. Lippman)
(这本书介绍一些C++的内部实现)
3。Advanced C++ Programming Styles And Idioms (James O. Coplien)
(这本书介绍一些C++编程的习惯用法)
4。 STL Tutorial and Reference Guide (David R. Musser) (Professor Musser
是我母校的教授,STL的创始人之一。这本书介绍Generic Programming
的思想和标准)。
5。 Design Patterns (Erich Gamma) (很精典的面向对象设计的组件和实例)
当然,看任何一本书的时候都要动手(试例程或做习题)。然后尝试着在课堂项目中把书中的方法试验一下,以加深印象。"
国外C++几乎所有的教材都被翻译为中文了,如果没有翻译的也已经有影印版出版。
TCPL和D&E
TCPL和D&E分别是《The C++ Programming Language》和《The Design and Evolution of C++》的简称,均出自Bjarne Stroustrup之手。说它们是C++语言圣经,并不为过。
中文名称:
《C++程序设计语言(特别版)》,机械工业出版社
《C++语言的设计和演化》,机械工业出版社
入门教程
Stanley B.Lippman, Josee Lajoie, C++ Primer (3rd Edition)
《C++ Primer (3RD)中文版》,中国电力出版社
Stanley B. Lippman, Essential C++
《Essential C++中文版》,华中科技大学出版社
《Essential C++(影印版)》,中国电力出版社
高效、健壮编程
《Effective C++中文版》,华中科技大学出版社
《More Effective C++中文版》,中国电力出版社
《Exceptional C++中文版》,中国电力出版社
《More Exceptional C++中文版》,华中科技大学出版社
模板和泛型编程
《C++ Templates全览(简体版)》,人民邮电出版社
《C++设计新思维:泛型编程与设计模式之应用》,华中科技大学出版社
标准库
《C++标准程序库:自修教程与参考手册》,华中科技大学出版社
《Effective STL(影印版)》,中国电力出版社
网络编程
《C++网络编程,卷1:运用ACE和模式消除复杂性》,华中科技大学出版社
《C++网络编程,卷2:基于 ACE 和框架的系统化复用》,电子工业出版社
杂项
《C++编程思想(第2版)第1卷:标准C++导引》,机械工业出版社
《C++编程思想(英文版 第2版)》,机械工业出版社
《C++沉思录》,人民邮电出版社
《深度探索C++对象模型》,华中科技大学出版社
《深度探索C++对象模型(影印版)》,中国电力出版社
C++工程领域的最高境界:
《设计模式:可复用面向对象软件的基础》,机械工业出版社
2005年10月30日
How To Ask Questions The Smart Way 提问的智慧
Copyright (C) 2001 by Eric S. Raymond 中文版Copyleft 2001 by D.H.Grand(nOBODY/Ginux)
英文版:http://www.tuxedo.org/~esr/faqs/smart-questions.html 感谢Eric的耐心指点和同意,本文才得以完成并发布,本指南英文版版权为Eric Steven Raymond所有,中文版版权由D.H.Grand[nOBODY/Ginux]所有。
目录
简介 提问之前 怎样提问 谨慎选择论坛 尽量使用邮件列表 用辞贴切,语法正确,拼写无误 用易读格式发送问题 使用含义丰富,描述准确的标题 精确描述,信息量大 话不在多 只说症状,不说猜想 按时间顺序列出症状 别要求私下答复 明白你想问什么 别问应该自己解决的问题 去除无意义的疑问 谦逊绝没有害处,而且常帮大忙 问题解决后,加个简短说明 如何理解答案 RTFM和STFW:别烦我啦 还是不懂 面对无礼
决不要象个失败者 三思而后问 好问题,坏问题 找不到答案怎么办
==== 简介 ====
在黑客世界里,当提出一个技术问题时,你能得到怎样的回答?这取决于挖出答案 的难度,同样取决于你提问的方法。本指南旨在帮助你提高发问技巧,以获取你最 想要的答案。
首先你必须明白,黑客们只偏爱艰巨的任务,或者能激发他们思维的好问题。如若 不然,我们还来干吗?如果你有值得我们反复咀嚼玩味的好问题,我们自会对你感 激不尽。好问题是激励,是厚礼,可以提高我们的理解力,而且通常会暴露我们以 前从没意识到或者思考过的问题。对黑客而言,“问得好!”是发自内心的大力称 赞。
尽管黑客们有蔑视简单问题和不友善的坏名声,有时看起来似乎我们对新手,对知 识贫乏者怀有敌意,但其实不是那样的。
我们不想掩饰对这样一些人的蔑视--他们不愿思考,或者在发问前不去完成他们应 该做的事。这种人只会谋杀时间--他们只愿索取,从不付出,无端消耗我们的时间 ,而我们本可以把时间用在更有趣的问题或者更值得回答的人身上。我们称这样的 人为“失败者”(由于历史原因,我们有时把它拼作“lusers”)。
我们也知道,很多人只想使用我们编写的软件,对技术细节没什么兴趣。对多数人 们而言,计算机不过是一个工具,一种达到目的的手段;他们有更重要的事情要做 ,有更重要的生活要过。我们明白这点,也并不奢望每个人都对另我们痴狂的技术 问题有兴致。然而,我们回答问题的风格是针对这样一群人--他们有兴趣,并且愿 意积极参与问题的解决。这点不会改变,也不应该改变;如果变了,我们将失去我 们引以为傲的效率。
我们在很大程度上属于志愿者,从繁忙的生活中抽出时间来解惑答疑,而且时常被 提问淹没。所以我们无情的滤掉一些话题,特别是抛弃那些看起来象失败者的家伙 ,以便更高效的利用时间来回答胜利者的问题。
如果你觉得我们过于傲慢的态度让你不爽,让你委屈,不妨设身处地想想。我们并 没有要求你向我们屈服--事实上,我们中的大多数人最喜欢公平交易不过了,只要 你付出小小努力来满足最起码的要求,我们就会欢迎你加入到我们的文化中来。但 让我们帮助那些不愿意帮助自己的人是没有意义的。如果你不能接受这种“歧视” ,我们建议你花点钱找家商业公司签个技术支持协议得了,别向黑客乞求帮助。
如果你决定向我们求助,当然不希望被视为失败者,更不愿成为失败者中的一员。 立刻得到有效答案的最好方法,就是象胜利者那样提问--聪明、自信、有解决问题 的思路,只是偶尔在特定的问题上需要获得一点帮助。
(欢迎对本指南提出改进意见。任何建议请E-mail至esr@thyrsus.com,然而请注 意,本文并非网络礼节的通用指南,我通常会拒绝无助于在技术论坛得到有用答案 的建议。) (当然,如果你写中文,最好还是寄到DHGrand@hotmail.com;-)
======== 提问之前 ========
在通过电邮、新闻组或者聊天室提出技术问题前,检查你有没有做到: 1. 通读手册,试着自己找答案。 2. 在FAQ里找答案(一份维护得好的FAQ可以包罗万象:)。 3. 在网上搜索(个人推荐google~~~)。 4. 向你身边精于此道的朋友打听。
当你提出问题的时候,首先要说明在此之前你干了些什么;这将有助于树立你的形 象:你不是一个妄图不劳而获的乞讨者,不愿浪费别人的时间。能说明你从这些操 作中学到了什么就更好了。如果提问者能从答案中学到东西,我们更乐于回答他的 问题。
周全的思考,准备好你的问题,草率的发问只能得到草率的回答,或者根本得不到 任何答案。越表现出在寻求帮助前为解决问题付出的努力,你越能得到实质性的帮 助。
小心别问错了问题。如果你的问题基于错误的假设,普通黑客(J. Random Hacker)通常会用无意义的字面解释来答复你,心里想着“蠢问题...”,希望着 你会从问题的回答(而非你想得到的答案)中汲取教训。
决不要自以为够资格得到答案,你没这种资格。毕竟你没有为这种服务支付任何报 酬。你要自己去“挣”回一个答案,靠提出一个有内涵的,有趣的,有思维激励作 用的问题--一个对社区的经验有潜在贡献的问题,而不仅仅是被动的从他人处索要 知识--去挣到这个答案。
另一方面,表明你愿意在找答案的过程中做点什么,是一个非常好的开端。“谁能 给点提示?”、“我这个例子里缺了什么?”以及“我应该检查什么地方?”比“ 请把确切的过程贴出来”更容易得到答复。因为你显得只要有人指点正确的方向, 你就有完成它的能力和决心。
======== 怎样提问 ========
------------ 谨慎选择论坛 ------------
小心选择提问的场合。如果象下面描述的那样,你很可能被忽略掉或者被看作失败 者: 1. 在风马牛不相及的论坛贴出你的问题 2. 在探讨高级技巧的论坛张贴非常初级的问题;反之亦然 3. 在太多的不同新闻组交叉张贴
黑客们通常砍掉问错地方的问题,以保护自己的社区不被大量无关帖子淹没。你不 会希望自己的帖子被这样砍掉吧。
总的说来,问题发到精心挑选的公众论坛,比发到封闭的小圈子更容易得到有用的 答案。这一现象有多种原因,其中之一是公众论坛有更多潜在的问题回答者;另一 个原因是公众论坛有更多的听众。黑客们更愿意让尽量多的人--而非有限的一两个 --从回答中受益。
---------------- 尽量使用邮件列表 ----------------
如果某项目有自己的开发邮件列表,要把问题发到这个邮件列表而不是某个开发者 ,即使你很清楚谁最能回答你的问题。仔细查看项目文档和项目主页,找到这个项 目的邮件列表地址,这样做的理由有四: 1. 任何值得问某位开发者的好问题,都值得向整个开发团体提出。反之,若你认 为这个问题不值得在邮件列表中提起,就没有理由用它来骚扰任何一位开发者。
2. 在邮件列表提问可以分担开发者的工作量。某位开发者(尤其当他是项目负责 人的情况下),可能忙得没时间回答你的问题。 3. 大多数邮件列表都有历史存档,而且都能在搜索引擎中检索到。人们可以从中 找到你的问题和答案,不用一遍又一遍在列表中发问。 4. 如果某个问题经常被提出,开发者可以据此改进文档或改进软件,以减少用户 的困惑。而如果问题总在私下提出,就不会有人对此有整体上的把握了。
如果你找不到项目的邮件列表地址,只能看到项目维护者的,那就写给维护者吧。 在这种情况下,也别以为邮件列表并不存在。在你的信中写明你已尽力寻找,仍无 法找到邮件列表。另外表明你不介意将此消息转给他人。(大多数人认为私信就应 该是私下的,即使并没有什么可保密的内容。允许你的消息被转寄给他人,给了收 信者一种处理你邮件的选择。)
---------------------------- 用辞贴切,语法正确,拼写无误 ----------------------------
我们从经验中发现,粗心的写作者通常也是马虎的思考者(我敢打包票)。回答粗 心大意者的问题很不值得,我们宁愿把时间耗在别处。
因此,明确充分表述你的问题非常重要。如果你嫌这样做麻烦,我们也会懒得搭理 你。注意推敲你的用辞,不一定要用呆板正式的语言--事实上,黑客文化的价值观 是不拘小节。准确的运用俚语和富有幽默感的语言,但别乱用;一定要能表明你在 思考,在关注。
正确的拼写,标点符号和大小写很重要。别把“its”和“it's”或者“loose”和 “lose”搞混淆了。别用全部大写的形式,这被视为粗鲁的大声叫嚷(全都用小写 也好不到哪儿去,因为这会给阅读带来困难。Alan Cox可以用全部小写,但你不行 )。
更一般的说,如果你的提问写得象个半文盲,你很有可能被忽视。如果写得象一个 窥客(破解爱好者)或者灰客(只会用现成工具的捣乱者)绝对是自己找死,保证 你除了无情的抵制什么也得不到(或者,最好的结局是得到一大堆挖苦嘲笑的“帮 助”)。
如果你在使用非母语的论坛提问,你可以犯点拼写和语法上的小错--但决不能在思 考上马虎(没错,我们能弄清两者的分别)。另外,除非你确切知道你的回答者会 使用什么语言,否则请用英文。匆匆忙忙的黑客往往简单的跳过他们看不懂的问题 ,而英文是网络上的工作语言。用英文可以降低你的问题未被阅读即遭抛弃的风险 。
------------------ 用易读格式发送问题 ------------------
如果人为造成你的提问难以阅读和理解,将会更容易被人忽略。因此你要: 1. 使用纯文本邮件,不要使用HTML(关掉HTML并不难)。 2. 通常可以附加MIME附件,但一定要有真正的内容(例如附加的源文件或者补丁 ),而不仅仅是你的邮件客户端产生的文件模板(例如你邮件的一份拷贝)。 3. 不要把所有问题放在不停换行的一整段中。(这将让答复的人难于回答其中一 部分问题,即使能回答所有问题,我也更希望条理清楚的一个一个来:)。很可能 收件人只能在80个字符宽度的文本显示器上读信,因此要相应的把行环绕模式设在 80字符以内。 4. 不要在英文论坛使用MIME Quoted-Printable编码发送;这种编码格式对ASCII 码不能表达的语言来说是非常必要的,但很多邮件代理不支持它,这时,满篇的“ =20”符号把文字分割开,既难看,又分散注意力。 5. 永远不要指望黑客会乐于阅读封闭所有权的文件格式,例如萎软的Word格式。 多数黑客对此的反应就象你在门口的阶梯上堆满热烘烘的猪粪(意即谁也不会踏进 你的门--译者注)。 6. 如果你通过一台安装Windows的电脑发送邮件,关闭萎软愚蠢的“智能引用”功 能。这能使你免于在邮件中夹带垃圾字符。
---------------------------- 使用含义丰富,描述准确的标题 ----------------------------
在邮件列表或者新闻组中,大约50字以内的主题标题是抓住资深专家注意力的黄金 时机。别用喋喋不休的“帮帮忙”(更别说“救命啊!!!!!”这样让人反感的 话)来浪费这个机会。不要妄想用你的痛苦程度来打动我们,别用空格代替问题的 描述,哪怕是极其简短的描述。
蠢问题: 救命啊!我的膝上机不能正常显示了!
聪明问题: XFree86 4.1下鼠标光标变形,Fooware MV1005的显示芯片。
如果你在回复中提出问题,记得要修改内容标题,表明里面有一个问题。一个看起 来象“Re:测试”或者“Re:新bug”的问题很难引起足够重视。另外,引用并删 减前文的内容,给新来的读者留下线索。
------------------ 精确描述,信息量大 ------------------
1. 谨慎明确的描述症状。 2. 提供问题发生的环境(机器配置、操作系统、应用程序以及别的什么)。 3. 说明你在提问前是怎样去研究和理解这个问题的。 4. 说明你在提问前采取了什么步骤去解决它。 5. 罗列最近做过什么可能有影响的硬件、软件变更。
尽量想象一个黑客会怎样反问你,在提问的时候预先给他答案。
Simon Tatham写过一篇名为《如何有效的报告Bug》的出色短文。强力推荐你也读 一读。
-------- 话不在多 --------
你需要提供精确有效的信息。这并不是要求你简单的把成吨的出错代码或者数据完 全转储摘录到你的提问中。如果你有庞大而复杂的测试条件,尽量把它剪裁得越小 越好。
这样做的用处至少有三点。第一,表现出你为简化问题付出了努力,这可以使你得 到回答的机会增加;第二,简化问题使你得到有用答案的机会增加;第三,在提炼 你的bug报告的过程中,也许你自己就能找出问题所在或作出更正。
------------------ 只说症状,不说猜想 ------------------
告诉黑客们你认为问题是怎样引起的没什么帮助。(如果你的推断如此有效,还用 向别人求助吗?),因此要确信你原原本本告诉了他们问题的症状,不要加进你自 己的理解和推论。让黑客们来诊断吧。
蠢问题: 我在内核编译中一次又一次遇到SIG11错误,我怀疑某条飞线搭在主板的走线上了 ,这种情况应该怎样检查最好?
聪明问题: 我自制的一套K6/233系统,主板是FIC-PA2007 (VIA Apollo VP2芯片组),256MB Corsair PC133 SDRAM,在内核编译中频频产生SIG11错误,从开机20分钟以后就 有这种情况,开机前20分钟内从没发生过。重启也没有用,但是关机一晚上就又能 工作20分钟。所有内存都换过了,没有效果。相关部分的典型编译记录如下...。
------------------ 按时间顺序列出症状 ------------------
对找出问题最有帮助的线索,往往就是问题发生前的一系列操作,因此,你的说明 应该包含操作步骤,以及电脑的反应,直到问题产生。在命令行操作的情况下,保 存一个操作记录(例如使用脚本工具),并且引用相关的大约20条命令会大有帮助 。
如果崩溃的程序有诊断选项(例如用-v转到详尽模式),试着仔细考虑选择选项以 在操作记录中增加有用的调试信息。
如果你的说明很长(超过四个段落),在开头简述问题会有所帮助,接下来按时间 顺序详述。这样黑客们就知道该在你的说明中找什么。
-------------- 别要求私下答复 --------------
黑客们认为解决问题应该有公开、透明的流程。只要任何更有见地的人注意到答案 的不完善或者不正确,这个最初的答案就可以和应该得到纠正。同时,通过能力和 知识被大家注意,被大家接受,回答问题者得到了应有的奖励。
如果你要求对方私下回答你,这既破坏了整个流程,也破坏了奖励制度。别提这要 求,这是回答者的权利,由他来选择是否私下答复--如果他选择这样做,通常是因 为他认为这个答案过于显而易见或者有不良的公开影响,别人不会感兴趣。
只有一种有限的例外:如果你预计将收到大量雷同的答复,你可以说:“把答案寄 给我,由我来汇总吧。”将邮件列表或者新闻组从大量重复的帖子中打救出来是很 有君子之风的--但请记住,履行自己关于汇总的承诺。
-------------- 明白你想问什么 --------------
漫无边际的提问近乎无休无止的时间黑洞。最能给你有用答案的人也正是最忙的人 (他们忙是因为要亲自完成大部分工作)。这样的人对无节制的时间黑洞不太感冒 ,因此也可以说他们对漫无边际的提问不大感冒。
如果你明确表述需要回答者做什么(提供建议,发送一段代码,检查你的补丁或是 别的),就最有可能得到有用的答案。这会定出一个时间和精力的上限,便于回答 者集中精力来帮你,这很凑效。
要理解专家们生活的世界,要把专业技能想象为充裕的资源,而回复的时间则是贫 乏的资源。解决你的问题需要的时间越少,越能从忙碌的专家口中掏出答案。
因此,优化问题的结构,尽量减少专家们解决它所需要的时间,会有很大的帮助 --这通常和简化问题有所区别。因此,问“我想更好的理解X,能给点提示吗?” 通常比问“你能解释一下X吗?”更好。如果你的代码不能工作,问问它有什么地 方不对,比要求别人替你修改要明智得多。
------------------------ 别问应该自己解决的问题 ------------------------
黑客们总是善于分辨哪些问题应该由你自己解决;因为我们中的大多数都曾自己解 决这类问题。同样,这些问题得由你来搞定,你会从中学到东西。你可以要求给点 提示,但别要求得到完整的解决方案。
---------------- 去除无意义的疑问 ----------------
别用无意义的话结束提问,例如“有人能帮我吗?”或者“有答案吗?”。首先: 如果你对问题的描述不很合适,这样问更是画蛇添足。其次:由于这样问是画蛇添 足,黑客们会很厌烦你--而且通常会用逻辑上正确的回答来表示他们的蔑视,例如 :“没错,有人能帮你”或者“不,没答案”。
---------------------------- 谦逊绝没有害处,而且常帮大忙 ----------------------------
彬彬有礼,多用“请”和“先道个谢了”。让大家都知道你对他们花费时间义务提 供帮助心存感激。
实话实说,虽然这不象合乎语法、清楚准确的描述,避免私有格式等等那么重要( 也不能用来替代它们);黑客一般更喜欢直接了当然而技术上敏锐的bug报告,而 不是彬彬有礼的废话(如果这让你迷惑不解,请记住,我们衡量一个问题价值的标 准是:它能让我们学会多少)。
然而,如果你有很多问题无法解决,礼貌将会增加你得到有用答案的机会。
(我们注意到,自从本指南发布后,从资深黑客处得到的唯一严重缺陷反馈,就是 对预先道谢这一条。一些黑客觉得“先谢了”的言外之意是过后就不会再感谢任何 人了。我们的建议是:都道谢。)
------------------------ 问题解决后,加个简短说明 ------------------------
问题解决后,向所有帮助过你的人发个说明,让他们知道问题是怎样解决的,并再 一次向他们表示感谢。如果问题在新闻组或者邮件列表中引起了广泛关注,应该在 那里贴一个补充说明。
补充说明不必很长或是很深入;简单的一句“你好,原来是网线出了问题!谢谢大 家--Bill”比什么也不说要强。事实上,除非结论真的很有技术含量,否则简短可 爱的小结比长篇学术论文更好。说明问题是怎样解决的,但大可不必将解决问题的 过程复述一遍。
除了表示礼貌和反馈信息以外,这种补充有助于他人在邮件列表/新闻组/论坛中搜 索对你有过帮助的完整解决方案,这可能对他们也很有用。
最后(至少?),这种补充有助于所有提供过帮助的人从中得到满足感。如果你自 己不是老手或者黑客,那就相信我们,这种感觉对于那些你向他们求助的导师或者 专家而言,是非常重要的。问题久拖未决会让人灰心;黑客们渴望看到问题被解决 。好人有好报,满足他们的渴望,你会在下次贴出新问题时尝到甜头。
============ 如何理解答案 ============
-------------------- RTFM和STFW:别烦我啦 --------------------
有一个古老而神圣的传统:如果你收到“RTFM (Read The Fucking Manual)”的 回复,回答者认为你应该去读TMD手册。当然,基本上他是对的,你应该读一读。
RTFM有一个年轻的亲戚。如果答案是“STFW (Search The Fucking Web)”,回 答者认为你应该到TMD的网上去搜索。基本上,他也是对的,你就去找吧。
通常,用这两句之一回答你的人会给你一份包含你需要内容的手册或者一个网址, 而且他们打这些字的时候正在阅读着。这些答复意味着回答者认为(1). 你需要的 信息非常容易获得;(2). 你自己去搜索这些信息比灌给你能让你学到更多。
别为这个而不爽;依照黑客的标准,他没有对你的要求视而不见,已经能大致能表 示对你的关注。你应该对他祖母般的慈祥表示感谢。
---------- 还是不懂 ----------
如果你不是很理解答案,别立刻要求对方解释。象你以前试着自己解决问题时那样 (利用手册,FAQ,网络,身边的高手),去理解它。如果你真的需要对方解释, 记得表现出你已经学到了点什么。
比方说,如果我回答你:“看来似乎是zEntry被阻塞了;你应该先清除它。”,然 后:
一个很糟的后续问题:“zEntry是什么?”
聪明的问法应该是这样:“哦~~~我看过帮助了但是只有-z和-p两个参数中提到了 zEntry而且还都没有清楚的解释:<你是指这两个中的哪一个吗?还是我看漏了什么 ?”
-------- 面对无礼 --------
黑客圈子里很多貌似粗鲁的言行并非有意冒犯。更恰当的说,这是直率、不说废话 的沟通方式的产物,这种沟通方式源于人们关注问题的解决--多过让人感受温暖亲 情然而却依旧糊里糊涂--的天性。
如果你觉得受到粗鲁的对待,请保持冷静。如果真有人表现粗野,通常会有列表/ 新闻组/论坛的长辈找他谈心,如果没有这样,而你又大发脾气,则很可能对方的 言行是黑客社区行为规范许可内,而你被认为是有过错的。这会不利于你得到信息 或者帮助。
另一方面,你偶尔也会无缘无故有粗野的言行和心态。上述现象的另一面是,人们 允许狠狠打击真正的冒犯者,用尖刻的言语剖析他们的不当言行。如果你真决定这 样做,先仔细又仔细的掂量一下你自己的分量。合理的粗鲁与发动一场无意义的论 战之间只隔了一条细细的线,冒冒失失撞上去的黑客不在少数;如果你是新手或者 门外汉,不犯这种错的机会是很渺茫的。如果你想得到信息而不是来胡闹,别冒险 回复,最好把手从键盘上拿开。
(有些人声称多数黑客有孤僻症或者社交障碍综合征的轻度症状,而且确实缺少部 分有助“常人”进行社交行为的脑组织结构。这也许是真的,也许不是。如果你自 己不是黑客,那么,把我们想象成脑部有缺陷的人有助你面对我们的古怪。有话直 说,我们无所谓;我们乐于按自己的想法生活,而且总是对医学概念持相当怀疑的 态度。)
在下一节里,我们将谈论另一个话题;当你行差踏错时可能遇到的“无礼”。
================ 决不要象个失败者 ================
很有可能,你在黑客社区的论坛会受到很多公开的攻击--用本文提到的各种方式或 类似的方法,而且很可能会有各式各样的旁敲侧击来告诉你你有多讨厌。
如果噩梦成真,你能做的最糟的事就是为此发牢骚,抱怨受到人身攻击,要求对方 道歉,尖叫,屏住呼吸,威胁要控诉对方,向他老板告状,不掀起马桶座圈,等等 等等。然而,你应该这样:
由它去吧,这没什么大不了的。实际上这么做是恰当的和有益的(主要是有利身心 健康:)。
社区的规范不靠社区,而是靠积极推行它们的人们来维护,这种维护是公开的,显 而易见的。别抱怨说一切批评都应该通过私信传送,它本来就不该那样。当别人指 出你的话有错误,或者他有不同观点的时候,坚持认为他在羞辱你是没有用的。这 些都是失败者的态度。
有那么一些黑客论坛,出于对高度自谦的误解,禁止参与者张贴专给人找茬的帖子 ,而且被告知“如果不愿帮助用户,那就闭嘴。”,他们认为,引开参与者的话题 ,只会使得他们陶醉在毫无意义的喋喋不休中,从而失去了技术论坛的意义。
夸张的“友善”(以那种方式)还是有用的帮助:你自己选择吧。
记住:当黑客说你很烦人,(无论用多么粗暴的语言)警告你别再那样做了,他的 本意并非是针对(1)你,以及(2)他的社区。他本来可以轻易的忽略你,把你从他的 视线中抹去。如果你无法接受要向他表示感激,至少应该表现出你的气度,别抱怨 ,别期望只因为你是新人,你有戏剧般的敏感脆弱的神经和自封的权利,而受到易 碎玩偶般的特别对待。
========== 三思而后问 ==========
以下是几个经典蠢问题,以及黑客在拒绝回答时的心中所想:
问题:我能在哪找到X程序? 问题:我的程序/配置/SQL申明没有用 问题:我的Windows有问题,你能帮我吗? 问题:我在安装Linux(或者X)时有问题,你能帮我吗? 问题:我怎么才能破解root帐号/窃取OP特权/读别人的邮件呢?
提问:我能在哪找到X程序? 回答:就在我找到它的地方啊蠢货--搜索引擎的那一头。天呐!还有人不会用 Google吗?
提问:我的程序(配置、SQL申明)没有用 回答:这不算是问题吧,我对找出你的真正问题没兴趣--如果要我问你二十个问题 才找得出来的话--我有更有意思的事要做呢。在看到这类问题的时候,我的反应通 常不外如下三种: 1. 你还有什么要补充的吗? 2. 真糟糕,希望你能搞定。 3. 这跟我有什么鸟相关?
提问:我的Windows有问题,你能帮我吗? 回答:能啊,扔掉萎软的垃圾,换Linux吧。
提问:我在安装Linux(或者X)时有问题,你能帮我吗? 回答:不能,我只有亲自在你的电脑上动手才能找到毛病。还是去找你当地的 Linux用户组寻求手把手的指导吧(你能在这儿找到用户组的清单)。
提问:我怎么才能破解root帐号/窃取OP特权/读别人的邮件呢? 回答:想要这样做,说明你是个卑鄙小人;想找个黑客帮你,说明你是个白痴!
============== 好问题,坏问题 ==============
最后,我举一些例子来说明,怎样聪明的提问;同一个问题的两种问法被放在一起 ,一种是愚蠢的,另一种才是明智的。
蠢问题:我可以在哪儿找到关于Foonly Flurbamatic的资料? 这种问法无非想得到“STFW”这样的回答。
聪明问题:我用Google搜索过“Foonly Flurbamatic 2600”,但是没找到有用的 结果。谁知道上哪儿去找对这种设备编程的资料? 这个问题已经STFW过了,看起来他真的遇到了麻烦。
蠢问题:我从FOO项目找来的源码没法编译。它怎么这么烂? 他觉得都是别人的错,这个傲慢自大的家伙
聪明问题:FOO项目代码在Nulix 6.2版下无法编译通过。我读过了FAQ,但里面没 有提到跟Nulix有关的问题。这是我编译过程的记录,我有什么做得不对的地方吗 ? 他讲明了环境,也读过了FAQ,还指明了错误,并且他没有把问题的责任推到别人 头上,这个家伙值得留意。
蠢问题:我的主板有问题了,谁来帮我? 普通黑客对这类问题的回答通常是:“好的,还要帮你拍拍背和换尿布吗?” , 然后按下删除键。
聪明问题:我在S2464主板上试过了X、Y和Z,但没什么作用,我又试了A、B和C。 请注意当我尝试C时的奇怪现象。显然边带传输中出现了收缩,但结果出人意料。 在多处理器主板上引起边带泄漏的通常原因是什么?谁有好主意接下来我该做些什 么测试才能找出问题? 这个家伙,从另一个角度来看,值得去回答他。他表现出了解决问题的能力,而不 是坐等天上掉答案。
在最后一个问题中,注意“告诉我答案”和“给我启示,指出我还应该做什么诊断 工作”之间微妙而又重要的区别。
事实上,后一个问题源自于2001年8月在Linux内核邮件列表上的一个真实的提问。 我(Eric)就是那个提出问题的人。我在Tyan S2464主板上观察到了这种无法解释 的锁定现象,列表成员们提供了解决那一问题的重要信息。
通过我的提问方法,我给了大家值得玩味的东西;我让人们很容易参与并且被吸引 进来。我显示了自己具备和他们同等的能力,邀请他们与我共同探讨。我告诉他们 我所走过的弯路,以避免他们再浪费时间,这是一种对他人时间价值的尊重。
后来,当我向每个人表示感谢,并且赞赏这套程序(指邮件列表中的讨论--译者注 )运作得非常出色的时候,一个Linux内核邮件列表(lkml)成员表示,问题得到 解决并非由于我是这个列表中的“名人”,而是因为我用了正确的方式来提问。
我们黑客从某种角度来说是拥有丰富知识但缺乏人情味的家伙;我相信他是对的, 如果我象个乞讨者那样提问,不论我是谁,一定会惹恼某些人或者被他们忽视。他 建议我记下这件事,给编写这个指南的人一些指导。
================ 找不到答案怎么办 ================
如果仍得不到答案,请不要以为我们觉得无法帮助你。有时只是看到你问题的人不 知道答案罢了。没有回应不代表你被忽视,虽然不可否认这种差别很难区分。
总的说来,简单的重复张贴问题是个很糟的想法。这将被视为无意义的喧闹。
你可以通过其它渠道获得帮助,这些渠道通常更适合初学者的需要。
有许多网上的以及本地的用户组,由狂热的软件爱好者(即使他们可能从没亲自写 过任何软件)组成。通常人们组建这样的团体来互相帮助并帮助新手。
另外,你可以向很多商业公司寻求帮助,不论公司大还是小(Red Hat和 LinuxCare就是两个最常见的例子)。别为要付费才能获得帮助而感到沮丧!毕竟 ,假使你的汽车发动机汽缸密封圈爆掉了--完全可能如此--你还得把它送到修车铺 ,并且为维修付费。就算软件没花费你一分钱,你也不能强求技术支持总是免费的 。
对大众化的软件,就象Linux之类而言,每个开发者至少会有上万名用户。根本不 可能由一个人来处理来自上万名用户的求助电话。要知道,即使你要为帮助付费, 同你必须购买同类软件相比,你所付出的也是微不足道的(通常封闭源代码软件的 技术支持费用比开放源代码软件要高得多,而且内容也不那么丰富)。
|