工程传奇 ------ 学徒布劳恩

维尔纳·冯·布劳恩(Wernher von Braun),V2飞弹的爸爸,红石导弹的爸爸,木星火箭的爸爸,土星系列火箭的爸爸,航天领域的先驱。在二战期间,是德国V2弹道导弹的主设计师;冷战期间,是美国航天发展的核心动力。如果没有他,美国人决不可能在短短的十年之内完成登月壮举。
在布劳恩的传记里,记载着一件有趣的事:布劳恩在大学里参加金工实习,为期一年。期间,师傅发给他们每人一个拳头大的铁疙瘩,要他们用钳工手段加工出一个 立方体来。所谓“钳工手段”,也就是拿锉刀锉。布劳恩他们按照师傅的嘱咐开始干。但当他们把完成的立方体给师傅看时,却被退回来,因为不符合要求,得重 做。如此一次又一次地重新加工,又一次又一次地被退回来。直到最后,铁块被锉成火柴盒大小的时候,终于通过了。
我大学里学的是机械,这金工实习也算是必修课。不过也就是3、4个礼拜的事,从未被要求把什么铁疙瘩给怎么样了。所谓钳工实习,也不过是把一小段方钢锯一 锯,锉一锉,打个眼,做成一把斩口榔头。也没什么严格的要求,基本上任何人3、4天都能通过。远远没有布劳恩经历的这种金工实习那么艰苦。
大概也就只有德国人会这样严格地培养工程师。但就是这种几乎不可理喻的做法,使得学生具有极其扎实的基本功,从而培养出能力超强的工程师来。
要说基本功很重要,这地球人都知道。但是如何加强基本功,则见仁见智。从布劳恩锉铁疙瘩的故事里,我们可以看到一个长久以来行之有效的手段——强化练习。 通过反复练习和实践,可以获得并强化知识与技能。一个学机械的学生,通过亲手加工一个物件,可以真切地了解材料的特性,工具的特点,以及很多教科书上无法 体会到的东西。哪怕就是那叮当作响的声音,也能让人对机加工有所领悟。而反复的强化训练,则可以在每次加工的时候逐步深刻地,更加全面地了解这些知识。

软件开发也是一种实践性很强的工作,扎实的基本功对于程序员而言,有着至关重要的作用。(除非你只想成为一个“代码农民工”)。经常会有一些新手抓耳挠腮 地无法解决一个问题,而一位老手则三下两下便将其摆平。绝大多数情况下,并非老手的智商高多少,而是他们拥有更多的经验和知识。这些经验和知识则是通过长 期训练和积累获得的。
很多新入行的程序员,为了使自己更快地成熟,大量的看书,却不时地流露出无所斩获的感叹。根本来说,也就是因为没有足够的实践训练所致。通过一定的练习,可以使我们对理论有更感性的认识,能够更快地吸收和理解事物。
前年,我心血来潮,打算认真学习编译原理。过去也看过一些书,但总是一头雾水。这回换了“龙书”,可还是找不着北。当时,为了先复习一下整体概念,wiki了一番。偶然间,发现一个比较语法分析生成器的条目,进而发现了GOLD。这个生成器本身没有很特别的地方,但它带了一个图形化的Builder。可以在其中撰写BNF,然后执行,解析一个样本。并且可以单步执行,看到每一个步骤。还能构造出相应的AST供查看之用。这就为我提供了一个很好的试验平台。
我在看书的同时,开始在Builder上练习。先从简单的四则运算开始,慢慢地增加难度。练习的过程比较辛苦,特别是开头,经常出错,找不到原因。随着练 习的深入,便慢慢地了解了语法解析的特性。直到有一天,实现了一个稍微复杂些的语法构造之后,我突然明白了LR的真正含义。
与此同时,我在一个项目里通过SAX读取xml。随着代码的不断深入,我开始对自己说:“哦,这东西看起来很像一个编译器啊,读到什么东西,就执行什么操 作。”想着想着,也是突然之间,我的脑子里划过一道闪电:“这‘读到什么东西’不就是解析么?这‘就执行什么操作’不就是Action么?‘龙书’上可都 写得明明白白,Syntax Directed Transaction啊,这回算是对上了。”
说实在的,如果不是Builder和那个读xml的任务,干看书,我可能永远无法理解编译原理的一些基本原理。(Builder功不可没,它把一段代码的解析一步步地展示出来,把整个解析过程的五脏六腑看了个一清二楚)。

但是,练习并非盲目地、机械地反复操练。每一次练习都必须有一定的目的和结果。每一次练习都必须从上次练习中的遇到的问题和不解开始,有目的地实践这些要 点。练习的过程中最重要的,却是思考。传统上,我们喜欢把动手(实践)和动脑(理论学习)分开,常常说:“这孩子动手能力很强。”言外之意:“这小子恐怕 读书不怎么样。”但实际上动手更需要动脑。且不说这手是靠脑指挥的,动手的过程中,如果没有正确的思考和分析,做出来的东西肯定是一坨烂屎。
在实践的时候,需要脑子不停地思考,甚至要比看书的时候更勤快。此时,脑子必须思考如何做?需要采取什么策略?背后的原理是什么?书上是怎么说的?等等。同时还需要不停地回忆过去所学的一些原理和理论。一样东西,只有用到的时候,才会真正试图去掌握它。
而且,练习也是一个迭代的过程。每次练习都有可能发现新的问题和不解,那么下一次就可以针对性地设计相应的练习。布劳恩他们每次提交作品的时候,师傅都会找出种种问题。而每次他们都必须找出造成问题的原因,加以改进,直到达到要求。
最近一位同事询问起关于函数的引用参数和值参数之间的差别。我颇费了一番口舌,也没能完全讲清其中的要点。倒不是他资质愚钝(他很好学,理解力也不错),也不是我笨嘴拙舌(我自信还是能说会道的),实在是这个问题很难几句话说清楚。最佳的解决方案,还是练习。
可以在C++里建两个函数,然后分别用int和int*做参数传递。然后编译,单步到函数里,看这个参数的地址和内容。此时如果回忆一下书上指针的定义, 很快就会发现两者的区别。如果还不明白,就在函数里为参数赋值,将参数解引用后赋值。这就很明显了。但是问题来了,int和int*类型不同,它们的访问 方式也不同,但在其他语言,象C#和java,引用和值的访问形式是一样的,它们的行为依然令人费解。这就需要做下一轮练习。
接下来,用int&做参数,代替int*。同样编译,然后单步到函数里。然后查看参数的地址和内容,此时便可以发现明显的区别。接着,还可以比较 调用函数时用的实参的地址和函数内参数的地址,便可以清晰地发现,int&参数是披着int外皮的指针。如果还不明白,可以再从另一个角度分析。
如果传递值,那么就需要把整个对象复制到函数的参数里,而传递引用,则只需要赋值指向对象的引用(指针)。于是,可以做一个类,放一个复制构造函数,并且 在其中设一个断点。然后用分别这个类和类的引用做参数,构建两个函数。再编译,运行。当单步进入函数式,用类做参数类型的函数调用触发了那个断点,而用类 引用做参数类型的函数调用则不会触发断点。这从另一个侧面证明了值类型参数是通过复制对象传递参数的,而引用类型参数则只是复制了对象的引用(指针)。
在此基础上,可以用不同的类型,不同的形式做些练习,直到突然间灵光一现,把心中的疑惑一扫而空。

实践和理论往往就是物和影的关系。有了理论,才有实践的方向;有了实践,才会强化对理论的认识。理论是实践的“精神领袖”,而实践是学习和巩固理论的“力 量倍增器”。通过合理的方法,可以用最高的效率方式实现对理论的理解。通过循序渐进的练习,则可以逐步地分解问题,从不同侧面观察和理解一个理论。我们说 学习有很多种方法,而实践在很多情况下是最有效的学习方式。

你可能感兴趣的:(读书)