Tairan's Story

「知之真切笃实处即是行,行之明觉精察处即是知,知行工夫不可离。」


Fire And Motion

这是一篇从Joel on Software转载的文章,也许大家在这里能得到一些启示: 中文 英文

行进中开火
作者: 周思博 (Joel Spolsky)
译: Siyan Li 李思延
编辑: Paul May 梅普華
2002年1月6日

时不时,总有一阵儿,我什么事也干不了。

我也去办公厅,东瞄瞄,西看看,每十秒钟查一次电子邮件,网上逛一圈。也许干点儿象付运通卡账单之类不需要大脑的事。不过要回去哗啦哗啦写程序,可没门儿。

这种不出活的状态,一般通常会持续一两天。在我的软件开发生涯中也有过几个星期干不了活的时候。就像他们说的,我不在状态,我进入不了情况,我找不到组织。

人人都有情绪波动,有的人温和一些,有的响动大点儿,也有的可以整个乱套。但不管怎么着,那段不出活期似乎总是跟忧郁有点儿关系。

我不由得联想到那些专家说,人们基本上控制不了自己吃什么。任何节食计划都长不了。大家总是悠回各自的正常体重。也许作为一个软件工程师,我也不能控制什么时候最能出活。我唯一希望的就是发呆那段能被哗哗干活那段扯平,最终还能混碗饭吃。

自从我干上软件开发这一行起,我平均每天只有两三个的高效时间。这真让我头大。我在微软实习的时候,另外一个实习生告诉我,他每天12点上班,5点下班。5个钟头还包括午餐时间,但他的同事还对他特别满意。因为他干的活比一般人都多。其实我也一样。我每天只有两三个小时的高效时间。看着别人那么卖力的干,还有点不好意思。不过呢,我总是组里出活最多的。由此可见,“人件理论”和极限编程都坚持不加班,每周只干40小时,还是有点道理的。他们都清楚这么做不会降低一个小组的生产能力。

每天只能干两小时还没让我太担心,真让我担心的是完全干不了活的那些天。

我老想这是怎么回事儿。我努力回忆我出活最多的时候。估计是微软把我搬到一间漂亮的新办公室的时候。舒适豪华的办公室,窗外风景如画,窗对面樱桃花开满了石头堆砌的庭院。所有的一切都那么恰到好处。我马不停蹄地干好好几个月,一口气把Excel Basic的详细设计搞定。用象纪念碑那么高的一叠纸,详细描素了一个超大型目标模型和编程环境,工作之细致,令人难以置信。我自始至终就没停过手。去波士顿参加MacWorld I的时候,我都带着一台手提电脑,坐在哈佛商学院的大阳台上把Windows类别的所有文件都写完了。

按步就班并不难。通常我一天是这样度过的:1,去上班。2,查电子邮件和上网等等。 3,考虑是否应该吃完中饭在开始干活。4,吃完中饭回来。5,查电子邮件逛网。6,终于决定应该开始工作了。7,查电子邮件逛网,东瞄瞄,西看看。8,再次决定确实应该开始开始干活了。9,打开该死的编辑器。10,一直会些程序学到晚上7:30,写到忘记时间。

在以上第8步和第9步之间似乎有点缺陷,因为我不是每次都能顺利地执行下去。

对我来说,启动是唯一的难题。静止物体在不受外力作用的情况下会保持静止。大脑里有些物质的质量大得不可思议,让它加速太难了。但是只要速度上去了,在全速行使的情况下,倒不用使什么劲就能继续走下去。就象骑着自行车去作一次自费横穿美国的旅行,一开始,你根本想象不出要花那么多时间让车轮动起来,可是一旦动起来了,让它们继续转就不是一件很难的事了。

也许高效率的关键就:启动起来。配对编程法之所以成功,说不定就靠两个人在一起,互相强迫对方启动起来。

我在以色烈当伞兵时,一次,有个将军来给我们讲实战战术。他告诉我们,步兵战术其实只有一种:行进中开火。你一边开火一边朝着敌人冲过去,火力让敌人抬不起头来,不能朝你开火 (当一个军人喊:“掩护我”的时候,他的意思就是“在我冲过街时候,你朝敌人猛烈开火,迫使他猫起来,没法朝我开火)。前进了,你就可以占领阵地,接近敌人,这样你的胜算要大的多。你要是不往前冲,敌人就有时间来搞清楚形势,这可不妙。你要是不开火,敌人就要朝你开火,撂倒你。

我很长一段时间都在想着这个教导。我想通了不论是战斗机空中格斗还是大规模舰队攻击,大部份军事战略战术都是以行进中开火作为基础的。我又化了十五年时间才想通了行进中开火也是一个人在现实生活中成功的基本原则。你每天都得往前进点儿,不用想你写的程序怎么差劲,怎么卖不出去,只要你不停地写,不停地改,滴水也能穿石。同时, 要注意你的竞争对手朝你开火。他们是不是想让你全心全意应付他们的扫射,好让你往前走不了呢?

想想这些年来,微软开发出来的资料存取方法,从OBDC,RDO,DAO,ADO,OLEDB直到现在的 ADO,.NET,不停翻新,技术上有必要吗?还是因为那个设计组实在蹩脚,每过他妈一年就得重新发明一遍资料存取技术?(实际上可能真是)。它最终的效果其实是一道掩护火力,让竞争者别无选择,只能把本来该用来开发新功能的宝贵时间都用来移植和升级了。仔细看看软件行业,干得好的公司对那些对大公司都依赖最少,不用把所有精力都用来为赶潮流而把程序重写一遍,还得修改那些只有在Windows XP上才会出现的缺陷。那些花太多时间去猜测微软未来发展方向的公司,日子都好过不了。有些人见了.NET就发怵,忍不住要按.NET来完全重建自己的体系结构,以为自己别无选择。哥门儿,看清楚了,微软是在朝你开火呢,而且这只是掩护火力。这游戏就是这么玩儿的。这样一来,他们就可以大步朝前走,而你却不能。你要支持Hailstorm 吗?SOAP呢?还有RDF?是因为你的顾客需要,所以你支持它们?还是因为有人朝你开火而你觉得应该还击?大公司的营销部都懂火力掩护。他们到客人那儿就说,“你们不一定非买我们的。谁的产品最好您就应该买谁的。不过,我们想提醒您,在下单之前最好先确认他们支持(XML/ SOAP/CDE/J2EE)。否则你们就会被他们的技术套牢。”。等到小公司去向这个客户推销的时候,那个听话的CTO就会问他们:“你们有J2EE吗?”。他们回去就只好不管卖不卖得掉,都埋头打造他们的J2EE。他们也就再没有机会来展示自己的特色了。其实,这只不过是个打勾功能。因为有个打勾拦在那儿空着,你就必须有这个功能。其实谁都不需要它。这就是火力掩护。

对于我这样的小公司来说,行进中开火意味着两件事。别跟时间过不去,同时你还得每天都进步。天不负苦心人,你终有出头的一天。我昨天花了一天时间只不过让FogBUGZ的颜色稍微好看点。这不要紧,只要不停步。最重要的是,我们的软件越来越好,客人越来越多。在我们达到Oracle 的规模之前,我们并不需要通盘战略。我们只需要每天早晨到办公室来,别多想,打开编程器。

本文最先用英文出版,题为 Fire and Motion

No Comments » | Tags: | Categories: Technology 转载, 参考消息, 精彩文章
When A Child is Born

《你丫真狠》 我们需要这样的好电影!

电影很好看,我想说的是,如果上帝真的存在的话,那么蛇引诱夏娃吃苹果也是真的夏娃最终还是抵挡不住蛇的诱惑!男人遇到这样的事情,只能选择无奈的离开。

No Comments » | Tags: | Categories: 其他, 参考消息, 文学
编程不是构建软件,而是设计软件。– 源码就是设计

什么是软件工程中最重要的产出?毫无疑问是代码。代码是给人读的,偶尔让机器运行一下。为了给读代码的人提供便利,我们就需要给代码一些自描述信息(Meta)。这些Meta信息就是相关的文档,注释等。关于是否需要文档在javaeye还有很激烈的讨论。

下面让我们看看大师是怎么说的:英文原版

至今,我仍能记起当我顿悟并最终产生下面文章时所在的地方。那是1986年的夏天,我在加利福尼亚中国湖海军武器中心担任临时顾问。在这期间,我有幸参加了一个关于Ada的研讨会。讨论当中,有一位听众提出了一个具有代表性的问题,“软件开发者是工程师吗?”我不记得当时的回答,但是我却记得当时并没有真正解答这个问题。于是,我就退出讨论,开始思考我会怎样回答这样一个问题。现在,我无法肯定当时我为什么会记起几乎10年前曾经在Datamation杂志上阅读过的一篇论文,不过促使我记起的应该是后续讨论中的某些东西。这篇论文阐述了工程师为什么必须是好的作家(我记得该论文谈论就是这个问题——好久没有看了),但是我从该论文中得到的关键一点是:作者认为工程过程的最终结果是文档。换句话说,工程师生产的是文档,不是实物。其他人根据这些文档去制造实物。于是,我就在困惑中提出了一个问题,“除了软件项目正常产生的所有文档以外,还有可以被认为是真正的工程文档的东西吗?”我给出的回答是,“是的,有这样的文档存在,并且只有一份——源代码。”

把源代码看作是一份工程文档——设计——完全颠覆了我对自己所选择的职业的看法。它改变了我看待一切事情的方式。此外,我对它思考的越多,我就越觉得它阐明了软件项目常常遇到的众多问题。更确切地说,我觉得大多数人不理解这个不同的看法,或者有意拒绝它这样一个事实,就足以说明很多问题。几年后,我终于有机会把我的观点公开发表。C++ Journal中的一篇有关软件设计的论文促使我给编辑写了一封关于这个主题的信。经过几封书信交换后,编辑Livleen Singh同意把我关于这个主题的想法发表为一篇论文。下面就是这篇文章。

——Jack Reecves, December,22,2001

什么是软件设计?
Jack W.Reeves, 1992

面向对象技术,特别是C++,似乎给软件界带来了不小的震动。出现了大量的论文和书籍去描述如何应用这项新技术。总的来说,那些关于面向对象技术是否只是一个骗局的问题已经被那些关于如何付出最小的努力即可获得收益的问题所替代。面向对象技术出现已经有一段时间了,但是这种爆炸式的流行却似乎有点不寻常。人们为何会突然关注它呢?对于这个问题,人们给出了各种各样的解释。事实上,很可能就没有单一的原因。也许,把多种因素的结合起来才能最终取得突破,并且这项工作正在进展之中。尽管如此,在软件革命的这个最新阶段中,C++本身看起来似乎成为了一个主要因素。同样,对于这个问题,很可能也存在很多种理由,不过我想从一个稍微不同的视角给出一个答案:C++之所以变得流行,是因为它使软件设计变得更容易的同时,也使编程变得更容易。

虽然这个解释好像有点奇特,但是它却是深思熟虑的结果。在这篇论文中,我就是想要关注一下编程和程序设计之间的关系。近10年来,我一直觉得整个软件行业都没有觉察到做出一个软件设计和什么是真正的软件设计之间的一个微妙的不同点。只要看到了这一点,我认为我们就可以从C++增长的流行趋势中,学到关于如何才能成为更好的软件工程师的意义深远的知识。这个知识就是,编程不是构建软件,而是设计软件。

几年前,我参见了一个讨论会,其中讨论到软件开发是否是一门工程学科的问题。虽然我不记得了讨论结果,但是我却记得它是如何促使我认识到:软件业已经做出了一些错误的和硬件工程的比较,而忽视了一些绝对正确的对比。其实,我认为我们不是软件工程师,因为我们没有认识到什么才是真正的软件设计。现在,我对这一点更是确信无疑。

任何工程活动的最终目标都是某些类型的文档。当设计工作完成时,设计文档就被转交给制造团队。该团队是一个和设计团队完全不同的群体,并且其技能也和设计团队完全不同。如果设计文档正确地描绘了一个完整的设计,那么制造团队就可以着手构建产品。事实上,他们可以着手构建该产品的许多实物,完全无需设计者的任何进一步的介入。在按照我的理解方式审查了软件开发的生命周期后,我得出一个结论:实际上满足工程设计标准的惟一软件文档,就是源代码清单。

对于这个观点,人们进行了很多的争论,无论是赞成的还是反对的都足以写成无数的论文。本文假定最终的源代码就是真正的软件设计,然后仔细研究了该假定带来的一些结果。我可能无法证明这个观点是正确的,但是我希望证明:它确实解释了软件行业中一些已经观察到的事实,包括C++的流行。

在把代码看作是软件设计所带来的结果中,有一个结果完全盖过了所有其他的结果。它非常重要并且非常明显,也正因为如此,对于大多数软件机构来说,它完全是一个盲点。这个结果就是:软件的构建是廉价的。它根本就不具有昂贵的资格;它非常的廉价,几乎就是免费的。如果源代码是软件设计,那么实际的软件构建就是由编译器和连接器完成的。我们常常把编译和连接一个完整的软件系统的过程称为“进行一次构建”。在软件构建设备上所进行的主要投资是很少的——实际需要的只有一台计算机、一个编辑器、一个编译器以及一个连接器。一旦具有了一个构建环境,那么实际的软件构建只需花费少许的时间。编译50 000行的C++程序也许会花费很长的时间,但是构建一个具有和50 000行C++程序同样设计复杂性的硬件系统要花费多长的时间呢?

把源代码看作是软件设计的另外一个结果是,软件设计相对易于创作,至少在机械意义上如此。通常,编写(也就是设计)一个具有代表性的软件模块(50至100行代码)只需花费几天的时间(对它进行完全的调试是另外一个议题,稍后会对它进行更多的讨论)。我很想问一下,是否还有任何其他的学科可以在如此短的时间内,产生出和软件具有同样复杂性的设计来,不过,首先我们必须要弄清出如何来度量和比较复杂性。然而,有一点是明显的,那就是软件设计可以 极为迅速地变得非常庞大。

假设软件设计相对易于创作,并且在本质上构建起来也没有什么代价,一个不令人吃惊的发现是,软件设计往往是难以置信的庞大和复杂。这看起来似乎很明显,但是问题的重要性却常常被忽视。学校中的项目通常具有数千行的代码。具有10 000行代码(设计)的软件产品被它们的设计者丢弃的情况也是有的。我们早就不再关注于简单的软件。典型的商业软件的设计都是由数十万行代码组成的。许多软件设计达到了上百万行代码。另外,软件设计几乎总是在不断地演化。虽然当前的设计可能只有几千行代码,但是在产品的生命期中,实际上可能要编写许多倍的代码。

尽管确实存在一些硬件设计,它们看起来似乎和软件设计一样复杂,但是请注意两个有关现代硬件的事实。第一,复杂的硬件工程成果未必总是没有错误的,在这一点上,它不存在像软件那样让我们相信的评判标准。多数的微处理器在发售时都具有一些逻辑错误:桥梁坍塌,大坝破裂,飞机失事以及数以千计的汽车和其他消费品被召回——所有的这些我们都记忆犹新,所有的这些都是设计错误的结果。第二,复杂的硬件设计具有与之对应的复杂、昂贵的构建阶段。结果,制造这种系统所需的能力限制了真正能够生产复杂硬件设计公司的数目。对于软件来说,没有这种限制。目前,已经有数以百计的软件机构和数以千计的非常复杂的软件系统存在,并且数量以及复杂性每天都在增长。这意味着软件行业不可能通过仿效硬件开发者找到针对自身问题的解决办法。倘若一定要说出有什么相同之处的话,那就是,当CAD和CAM可以做到帮助硬件设计者创建越来越复杂的设计时,硬件工程才会变得和软件开发越来越像。

设计软件是一种管理复杂性的活动。复杂性存在于软件设计本身之中,存在于公司的软件机构之中,也存在于整个软件行业之中。软件设计和系统设计非常相似。它可以跨越多种技术并且常常涉及多个学科分支。软件的规格说明往往不固定、经常快速变化,这种变化常常在正进行软件设计时发生。同样,软件开发团队也往往不固定,常常在设计过程的中间发生变化。在许多方面,软件都要比硬件更像复杂的社会或者有机系统。所有这些都使得软件设计成为了一个困难的并且易出错的过程。虽然所有这些都不是创造性的想法,但是在软件工程革命开始将近30年后的今天,和其他工程行业相比,软件开发看起来仍然像是一种未受过训练(undisciplined)的技艺。

一般的看法认为,当真正的工程师完成了一个设计,不管该设计有多么复杂,他们都非常确信该设计是可以工作的。他们也非常确信该设计可以使用公认的技术建造出来。为了做到这一点,硬件工程师花费了大量的时间去验证和改进他们的设计。例如,请考虑一个桥梁设计。在这样一个设计实际建造之前,工程师会进行结构分析——他们建立计算机模型并进行仿真,他们建立比例模型并在风洞中或者用其他一些方法进行测试。简而言之,在建造前,设计者会使用他们能够想到的一切方法来证实设计是正确的。对于一架新型客机的设计来说,情况甚至更加严重;必须要构建出和原物同尺寸的原型,并且必须要进行飞行测试来验证设计中的种种预计。

对于大多数人来说,软件中明显不存在和硬件设计同样严格的工程。然而,如果我们把源代码看做是设计,那么就会发现软件工程师实际上对他们的设计做了大量的验证和改进。软件工程师不把这称为工程,而称它为测试和调试。大多数人不把测试和调试看作是真正的“工程”——在软件行业中肯定没有被看作是。造成这种看法的原因,更多的是因为软件行业拒绝把代码看作设计,而不是任何实际的工程差别。事实上,试验模型、原型以及电路试验板已经成为其他工程学科公认的组成部分。软件设计者之所以不具有或者没有使用更多的正规方法来验证他们的设计,是因为软件构建周期的简单经济规律。

第一个启示:仅仅构建设计并测试它比做任何其他事情要廉价一些,也简单一些。我们不关心做了多少次构建——这些构建在时间方面的代价几乎为零,并且如果我们丢弃了构建,那么它所使用的资源完全可以重新利用。请注意,测试并非仅仅是让当前的设计正确,它也是改进设计的过程的一部分。复杂系统的硬件工程师常常建立模型(或者,至少他们把设计用计算机图形直观地表现出来)。这就使得他们获得了对于设计的一种“感觉”,而仅仅去检查设计是不可能获得这种感觉的。对于软件来说,构建这样一个模型既不可能也无必要。我们仅仅构建产品本身。即使正规的软件验证可以和编译器一样自动进行,我们还是会去进行构建/测试循环。因此,正规的验证对于软件行业来说从来没有太多的实际意义。

这就是现今软件开发过程的现实。数量不断增长的人和机构正在创建着更加复杂的软件设计。这些设计会被先用某些编程语言编写出来,然后通过构建/测试循环进行验证和改进。过程易于出错,并且不是特别的严格。相当多的软件开发人员并不想相信这就是过程的运作方式,也正因为这一点,使问题变得更加复杂。

当前大多数的软件过程都试图把软件设计的不同阶段分离到不同的类别中。必须要在顶层的设计完成并且冻结后,才能开始编码。测试和调试只对清除建造错误是必要的。程序员处在中间位置,他们是软件行业的建造工人。许多人认为,如果我们可以让程序员不再进行“随意的编码(hacking)”并且按照交给他们的设计去进行构建(还要在过程中,犯更少的错误),那么软件开发就可以变得成熟,从而成为一门真正的工程学科。但是,只要过程忽视了工程和经济学事实,这就不可能发生。

例如,任何一个现代行业都无法忍受在其制造过程中出现超过100%的返工率。如果一个建造工人常常不能在第一次就构建正确,那么不久他就会失业。但是在软件业中,即使最小的一块代码,在测试和调试期间,也很可能会被修正或者完全重写。在一个创造性的过程中(比如:设计),我们认可这种改进不是制造过程的一部分。没有人会期望工程师第一次就创建出完美的设计。即使她做到了,仍然必须让它经受改进过程,目的就是为了证明它是完美的。

即使我们从日本的管理方法中没有学到任何东西,我们也应该知道由于在过程中犯错误而去责备工人是无益于提高生产率的。我们不应该不断地强迫软件开发去符合不正确的过程模型,相反,我们需要去改进过程,使之有助于而不是阻碍产生更好的软件。这就是“软件工程”的石蕊测试。工程是关于你如何实施过程的,而不是关于是否需要一个CAD系统来产生最终的设计文档。

关于软件开发有一个压倒性的问题,那就是一切都是设计过程的一部分。编码是设计,测试和调试是设计的一部分,并且我们通常认为的设计仍然是设计的一部分。虽然软件构建起来很廉价,但是设计起来却是难以置信的昂贵。软件非常的复杂,具有众多不同方面的设计内容以及它们所导致的设计考虑。问题在于,所有不同方面的内容是相互关连的(就像硬件工程中的一样)。我们希望顶层设计者可以忽视模块算法设计的细节。同样,我们希望程序员在设计模块内部算法时不必考虑顶层设计问题。糟糕的是,一个设计层面中的问题侵入到了其他层面之中。对于整个软件系统的成功来说,为一个特定模块选择算法可能和任何一个更高层次的设计问题同样重要。在软件设计的不同方面内容中,不存在重要性的等级。最低层模块中的一个不正确设计可能和最高层中的错误一样致命。软件设计必须在所有的方面都是完整和正确的,否则,构建于该设计基础之上的所有软件都会是错误的。

为了管理复杂性,软件被分层设计。当程序员在考虑一个模块的详细设计时,可能还有数以百计的其他模块以及数以千计的细节,他不可能同时顾及。例如,在软件设计中,有一些重要方面的内容不是完全属于数据结构和算法的范畴。在理想情况下,程序员不应该在设计代码时还得去考虑设计的这些其他方面的内容。

但是,设计并不是以这种方式工作的,并且原因也开始变得明朗。软件设计只有在其被编写和测试后才算完成。测试是设计验证和改进过程的基础部分。高层结构的设计不是完整的软件设计;它只是细节设计的一个结构框架。在严格地验证高层设计方面,我们的能力是非常有限的。详细设计最终会对高层设计造成的影响至少和其他的因素一样多(或者应该允许这种影响)。对设计的各个方面进行改进,是一个应该贯穿整个设计周期的过程。如果设计的任何一个方面内容被冻结在改进过程之外,那么对于最终设计将会是糟糕的或者甚至无法工作这一点,就不会觉得奇怪了。

如果高层的软件设计可以成为一个更加严格的工程过程,那该有多好呀,但是软件系统的真实情况不是严格的。软件非常的复杂,它依赖于太多的其他东西。或许,某些硬件没有按照设计者认为的那样工作,或者一个库例程具有一个文档中没有说明的限制。每一个软件项目迟早都会遇到这些种类的问题。这些种类的问题会在测试期间被发现(如果我们的测试工作做得好的话),之所以如此是因为没有办法在早期就发现它们。当它们被发现时,就迫使对设计进行更改。如果我们幸运,那么对设计的更改是局部的。时常,更改会波及到整个软件设计中的一些重要部分(莫非定律)。当受到影响的设计的一部分由于某种原因不能更改时,那么为了能够适应影响,设计的其他部分就必须得遭到破坏。这通常导致的结果就是管理者所认为的“随意编码”,但是这就是软件开发的现实。

例如,在我最近工作的一个项目中,发现了模块A的内部结构和另一个模块B之间的一个时序依赖关系。糟糕的是,模块A的内部结构隐藏在一个抽象体的后面,而该抽象体不允许以任何方法把对模块B的调用合入到它的正确调用序列中。当问题被发现时,当然已经错过了更改A的抽象体的时机。正如所料,所发生的就是把一个日益增长的复杂的“修正”集应用到A的内部设计上。在我们还没有安装完版本1时,就普遍感觉到设计正在衰退。每一个新的修正很可能都会破坏一些老的修正。这是一个正规的软件开发项目。最后,我和我的同事决定对设计进行更改,但是为了得到管理层的同意,我们不得不自愿无偿加班。

在任何一般规模的软件项目中,肯定会出现像这样的问题,尽管人们使用了各种方法来防止它的出现,但是仍然会忽视一些重要的细节。这就是工艺和工程之间的区别。如果经验可以把我们引向正确的方向,这就是工艺。如果经验只会把我们带入未知的领域,然后我们必须使用一开始所使用的方法并通过一个受控的改进过程把它变得更好,这就是工程。

我们来看一下只是作为其中很小一点的内容,所有的程序员都知道,在编码之后而不是之前编写软件设计文档会产生更加准确的文档。现在,原因是显而易见的。用代码来表现的最终设计是惟一一个在构建/测试循环期间被改进的东西。在这个循环期间,初始设计保持不变的可能性和模块的数量以及项目中程序员的数量成反比。它很快就会变得毫无价值。

在软件工程中,我们非常需要在各个层次都优秀的设计。我们特别需要优秀的顶层设计。初期的设计越好,详细设计就会越容易。设计者应该使用任何可以提供帮助的东西。结构图表、Booch 图、状态表、PDL等等——如果它能够提供帮助,就去使用它。但是,我们必须记住,这些工具和符号都不是软件设计。最后,我们必须创建真正的软件设计,并且是使用某种编程语言完成的。因此,当我们得出设计时,我们不应该害怕对它们进行编码。在必要时,我们必须应该乐于去改进它们。

至今,还没有任何设计符号可以同时适用于顶层设计和详细设计。设计最终会表现为以某种编程语言编写的代码。这意味着在详细设计可以开始前,顶层设计符号必须被转换成目标编程语言。这个转换步骤耗费时间并且会引入错误。程序员常常是对需求进行回顾并且重新进行顶层设计,然后根据它们的实际去进行编码,而不是从一个可能没有和所选择的编程语言完全映射的符号进行转换。这同样也是软件开发的部分现实情况。

也许,如果让设计者本人来编写初始代码,而不是后来让其他人去转换语言无关的设计,就会更好一些。我们所需要的是一个适用于各个层次设计的统一符号。换句话说,我们需要一种编程语言,它同样也适用于捕获高层的设计概念。C++正好可以满足这个要求。C++是一门适用于真实项目的编程语言,同时它也是一个非常具有表达力的软件设计语言。C++允许我们直接表达关于设计组件的高层信息。这样,就可以更容易地进行设计,并且以后可以更容易地改进设计。由于它具有更强大的类型检查机制,所以也有助于检测到设计中的错误。这就产生了一个更加健壮的设计,实际上也是一个更好的工程化设计。

最后,软件设计必须要用某种编程语言表现出来,然后通过一个构建/测试循环对其进行验证和改进。除此之外的任何其他主张都完全没有用。请考虑一下都有哪些软件开发工具和技术得以流行。结构化编程在它的时代被认为是创造性的技术。 Pascal使之变得流行,从而自己也变得流行。面向对象设计是新的流行技术,而C++是它的核心。现在,请考虑一下那些没有成效的东西。CASE工具,流行吗?是的;通用吗?不是。结构图表怎么样?情况也一样。同样地,还有Warner-Orr图、Booch图、对象图以及你能想起的一切。每一个都有自己的强项,以及惟一的一个根本弱点——它不是真正的软件设计。事实上,惟一一个可以被普遍认可的软件设计符号是PDL,而它看起来像什么呢?

这表明,在软件业的共同潜意识中本能地知道,编程技术,特别是实际开发所使用的编程语言的改进和软件行业中任何其他东西相比,具有压倒性的重要性。这还表明,程序员关心的是设计。当出现更加具有表达力的编程语言时,软件开发者就会使用它们。

同样,请考虑一下软件开发过程是如何变化的。从前,我们使用瀑布式过程。现在,我们谈论的是螺旋式开发和快速原型。虽然这种技术常常被认为可以“消除风险” 以及“缩短产品的交付时间”,但是它们事实上也只是为了在软件的生命周期中更早地开始编码。这是好事。这使得构建/测试循环可以更早地开始对设计进行验证和改进。这同样也意味着,顶层软件设计者很有可能也会去进行详细设计。

正如上面所表明的,工程更多的是关于如何去实施过程的,而不是关于最终产品看起来的像什么。处在软件行业中的我们,已经接近工程师的标准,但是我们需要一些认知上的改变。编程和构建/测试循环是工程软件过程的中心。我们需要以像这样的方式去管理它们。构建/测试循环的经济规律,再加上软件系统几乎可以表现任何东西的事实,就使得我们完全不可能找出一种通用的方法来验证软件设计。我们可以改善这个过程,但是我们不能脱离它。

最后一点:任何工程设计项目的目标是一些文档产品。显然,实际设计的文档是最重要的,但是它们并非惟一要产生的文档。最终,会期望某些人来使用软件。同样,系统很可能也需要后续的修改和增强。这意味着,和硬件项目一样,辅助文档对于软件项目具有同样的重要性。虽然暂时忽略了用户手册、安装指南以及其他一些和设计过程没有直接联系的文档,但是仍然有两个重要的需求需要使用辅助设计文档来解决。

辅助文档的第一个用途是从问题空间中捕获重要的信息,这些信息是不能直接在设计中使用的。软件设计需要创造一些软件概念来对问题空间中的概念进行建模。这个过程需要我们得出一个对问题空间中概念的理解。通常,这个理解中会包含一些最后不会被直接建模到软件空间中的信息,但是这些信息却仍然有助于设计者确定什么是本质概念以及如何最好地对它们建模。这些信息应该被记录在某处,以防以后要去更改模型。

对辅助文档的第二个重要需要是对设计的某些方面的内容进行记录,而这些方面的内容是难以直接从设计本身中提取的。它们既可以是高层方面的内容,也可以是低层方面内容。对于这些方面内容中的许多来说,图形是最好的描述方式。这就使得它们难以作为注释包含在代码中。这并不是说要用图形化的软件设计符号代替编程语言。这和用一些文本描述来对硬件科目的图形化设计文档进行补充没有什么区别。

决不要忘记,是源代码决定了实际设计的真实样子,而不是辅助文档。在理想情况下,可以使用软件工具对源代码进行后期处理并产生出辅助文档。对于这一点,我们可能期望过高了。次一点的情况是,程序员(或者技术方面的编写者)可以使用一些工具从源代码中提取出一些特定的信息,然后可以把这些信息以其他一些方式文档化。毫无疑问,手工对这种文档保持更新是困难的。这是另外一个支持需要更具表达力的编程语言的理由。同样,这也是一个支持使这种辅助文档保持最小并且尽可能在项目晚期才使之变成正式的理由。同样,我们可以使用一些好的工具;不然的话,我们就得求助于铅笔、纸以及黑板。

总结如下:
实际的软件运行于计算机之中。它是存储在某种磁介质中的0和1的序列。它不是使用C++语言(或者其他任何编程语言)编写的程序。
程序清单是代表软件设计的文档。实际上把软件设计构建出来的是编译器和连接器。
构建实际软件设计的廉价程度是令人难以置信的,并且它始终随着计算机速度的加快而变得更加廉价。
设计实际软件的昂贵程度是令人难以置信的,之所以如此,是因为软件的复杂性是令人难以置信的,并且软件项目的几乎所有步骤都是设计过程的一部分。
编程是一种设计活动——好的软件设计过程认可这一点,并且在编码显得有意义时,就会毫不犹豫的去编码。
编码要比我们所认为的更频繁地显现出它的意义。通常,在代码中表现设计的过程会揭示出一些疏漏以及额外的设计需要。这发生的越早,设计就会越好。
因为软件构建起来非常廉价,所以正规的工程验证方法在实际的软件开发中没有多大用处。仅仅建造设计并测试它要比试图去证明它更简单、更廉价。
测试和调试是设计活动——对于软件来说,它们就相当于其他工程学科中的设计验证和改进过程。好的软件设计过程认可这一点,并且不会试图去减少这些步骤。
还有一些其他的设计活动——称它们为高层设计、模块设计、结构设计、构架设计或者诸如此类的东西。好的软件设计过程认可这一点,并且慎重地包含这些步骤。
所有的设计活动都是相互影响的。好的软件设计过程认可这一点,并且当不同的设计步骤显示出有必要时,它会允许设计改变,有时甚至是根本上的改变,
许多不同的软件设计符号可能是有用的——它们可以作为辅助文档以及工具来帮助简化设计过程。它们不是软件设计。
软件开发仍然还是一门工艺,而不是一个工程学科。主要是因为缺乏验证和改善设计的关键过程中所需的严格性。
最后,软件开发的真正进步依赖于编程技术的进步,而这又意味着编程语言的进步。C++就是这样的一个进步。它已经取得了爆炸式的流行,因为它是一门直接支持更好的软件设计的主流编程语言。
C++在正确的方向上迈出了一步,但是还需要更大的进步。

后 记

当我回顾几乎10年前所写的东西时,有几点让我印象深刻。第一点(也是和本书最有关的)是,现今,我甚至比那时更加确信我试图去阐述的要点在本质上的正确性。随后的一些年中,许多流行的软件开发方法增强了其中的许多观点,这支持了我的信念。最明显的(或许也是最不重要的)是面向对象编程语言的流行。现在,除了C++外,出现了许多其他的面向对象编程语言。另外,还有一些面向对象设计符号,比如:UML。我关于面向对象语言之所以得到流行是因为它们允许在代码中直接表现出更具表达力的设计的论点,现在看来有点过时了。

重构的概念——重新组织代码基础,使之更加健壮和可重用——同样也和我的关于设计的所有方面的内容都应该是灵活的并且在验证设计时允许改变的论点相似。重构只是提供了一个过程以及一组如何去改善已经被证实具有缺陷的设计的准则。

最后,文中有一个敏捷开发的总的概念。虽然极限编程是这些新方法中最知名的一个,但是它们都具有一个共同点:它们都承认源代码是软件开发工作中的最重要的产品。

另一方面,有一些观点——其中的一些我在论文中略微谈到过——在随后的一些年中,对我来说变得更加重要。第一个是构架,或者顶层设计的重要性。在论文中,我认为构架只是设计的一部分内容,并且在构建/测试循环对设计进行验证的过程中,构架需要保持可变。这在本质上是正确的,但是回想起来,我认为我的想法有点不成熟。虽然构建/测试循环可能揭示出构架中的问题,但是更多的问题是常常由于改变需求而表现出来的。 一般来说,设计软件是困难的,并且新的编程语言,比如:Java或者C++,以及图形化的符号,比如:UML,对于不知道如何有效地使用它的人来说,都没有多大的帮助。此外,一旦一个项目基于一个构架构建了大量的代码,那么对该构架进行基础性的更改,常常相当于丢弃掉该项目并重新开始一个,这就意味着该项目没有出现过。即使项目和机构在根本上接受了重构的概念,但是他们通常仍然不愿意去做一些看起来就像是完全重写的事情。这意味着第一次就把它作对(或者至少是接近对)是重要的,并且项目变得越大,就越要如此。幸运的是,软件设计模式有助于解决这方面问题。

还有其他一些方面的内容,我认为需要更多地强调一下,其中之一就是辅助文档,尤其是构架方面的文档。虽然源代码就是设计,但是试图从源代码中得出构架,可能是一个令人畏惧的体验。在论文中,我希望能够出现一些软件工具来帮助软件开发者自动地维护来自源代码的辅助文档。我几乎已经放弃了这个希望。一个好的面向对象构架通常可以使用几幅图以及少许的十几页文本描述出来。不过,这些图(和文本)必须集中于设计中的关键类和关系。糟糕的是,对于软件设计工具可能会变得足够聪明,以至于可以从源代码的大量细节中提取出这些重要方面的内容这一点,我没有看到任何真正的希望。这意味着还得必须由人来编写和维护这种文档。我仍然认为,在源代码完成后,或者至少是在编写源代码的同时去编文档,要比在编写源代码之前去编写文档更好一些。

最后,我在论文的最后谈到了C++是编程——并且因此是软件设计——艺术的一个进步,但是还需要更大的进步。就算我完全没有看到语言中出现任何真正的编程进步来挑战C++的流行,那么在今天,我会认为这一点甚至要比我首次编写它时更加正确。

——Jack Reeves, 2002年1月1日

No Comments » | Tags:, | Categories: Technology 转载, 参考消息, 精彩文章
[转]两分钟让你明白什么是ERP

ERP(Enterprise ResourcePlanning)企业资源计划系统,是指建立在信息技术基础上,以系统化的管理思想,为企业决策层及员工提供决策运行手段的管理平台。

一天中午,丈夫在外给家里打电话:“亲爱的老婆,晚上我想带几个同事回家吃饭可以吗?”(订货意向)

妻子:“当然可以,来几个人,几点来,想吃什么菜? ”

丈夫:“6个人,我们7点左右回来,准备些酒、烤鸭、番茄炒蛋、凉菜、蛋花汤……。你看可吗?”(商务沟通)

妻子:“没问题,我会准备好的。”(订单确认)

妻子记录下需要做的菜单(MPS计划),具体要准备的东西:鸭、酒、番茄、鸡蛋、调料……(BOM物料清单),发现需要:1只鸭蛋,5瓶酒,4个鸡蛋……(BOM展开),炒蛋需要6个鸡蛋,蛋花汤需要4个鸡蛋(共用物料)。

打开冰箱一看(库房),只剩下2个鸡蛋(缺料)。

来到自由市场,妻子:“请问鸡蛋怎么卖?”(采购询价)

小贩:“1个1元,半打5元,1打9.5元。”

妻子:“我只需要8个,但这次买1打。”(经济批量采购)

妻子:“这有一个坏的,换一个。”(验收、退料、换料)

回到家中,准备洗采、切菜、炒菜……(工艺线路),厨房中有燃气灶、微波炉、电饭煲……(工作中心)。

妻子发现拨鸭毛最费时间(瓶颈工序,关键工艺路线),用微波炉自己做烤鸭可能来不及(产能不足),于是阅览室在楼下的餐厅里买现成的(产品委外)。

下午4点,接到儿子的电话:“妈妈,晚上几个同学想来家里吃饭,你帮忙准备一下。”(紧急订单)

“好的,你们想吃什么,爸爸晚上也有客人,你愿意和他们一起吃吗?”

“菜你看着办吧,但一定要有番茄炒鸡蛋,我们不和大人一起吃,6:30左右回来。”(不能并单处理)

“好的,肯定让你们满意。”(订单确定)

“鸡蛋又不购了,打电话叫小贬送来。”(紧急采购)

6:30,一切准备就绪,可烤鸭还没送来,急忙打电话询问:“我是李太,怎么订的烤鸭还不送来?”(采购委外单跟催)

“不好意思,送货的人已经走了,可能是堵车吧,马上就会到的。”

门铃响了。

“李太太,这是您要的烤鸭。请在单上签一个字。”(验收、入库、转应付账款)

6:45,女儿的电话:“妈妈,我想现在带几个朋友回家吃饭可以吗?”(呵呵 ,又是紧急订购意向,要求现货)

“不行呀,女儿,今天妈已经需要准备两桌饭了,时间实在是来不及,真的非常抱歉,下次早点说,一定给你们准备好。”(哈哈,这就是ERP的使用局限,要有稳定的外部环境,要有一个起码的提前期 )。
…… ……

送走了所有客人,疲惫的妻子坐在沙发上对丈夫说:“亲爱的,现在咱们家请客的频率非常高,应该要买些厨房用品了(设备采购),最好能再雇个小保姆(连人力资源系统也有缺口了)。
丈夫:“家里你做主,需要什么你就去办吧。”(通过审核)

妻子:“还有,最近家里花销太大,用你的私房钱来补贴一下,好吗?”(最后就是应收货款的催要)

现在还有人不理解ERP吗?记住,每一个合格的家庭主妇都是生产厂长的有力竞争者

No Comments » | Tags: | Categories: Technology 转载, 参考消息
Decade of Programming Institute

十年学会编程

著者: Peter Norvig
翻译: Dai Yuwen

为何人人都这么着急?

信步走进任何一家书店,你会看到名为《如何在7天内学会Java》的书,还有各种各样类似的书:在几天内或几小时内学会Visual Basic, Windows, Internet等等,一眼望不到尽头。我在Amazon 上做了如下的 强力检索 :
pubdate: after 1992 and title: days and
(title: learn or title: teach yourself)

得到了248个结果。前78个都是计算机类书籍(第79个是 Learn Bengali in 30 days)。我用”hours”替换”days”,得到了类似的结果:更多的253书。前77本是计算机类书籍,第78本是 Teach Yourself Grammar and Style in 24 Hours。在前200本书中,有96% 是计算机类书籍。

结论是:要么人们都在忙忙地学习计算机,要么计算机比其它任何东西都容易学。没有书籍教你在几天内学会古典音乐、量子物理,或者是养狗。

让我们分析一下,象一本名为《三天内学会Pascal》的书意味着什么:

* 学习: 在三天里,你没有时间写一些重大的程序,并从成功或失败中得益。你没有时间与有经验的程序员合作,并理解在那样的环境下工作是怎么回事。一句话,你不会有时间学到太多东西。因此他们只能谈论一些肤浅的东西,而不是深入的理解。正如亚力山大教皇所说,浅尝辄止是危险的事情。

* Pascal: 在三天时间里,你可能学会Pascal的语法(如果你已经学过类似的语言),但你学不到更多的如何使用这些语法的知识。也就是说,假如你曾是个BASIC 程序员,你可以学着用Pascal语法写出BASIC风格的程序,但你不可能了解Pascal真正的好处(和坏处)。那么关键是什么? Alan Perlis 说过:“一种不改变你编程的思维方式的语言,不值得去学。” 一种可能的情况是:你必须学一点儿Pascal(或可能性更大的象Visual Basic 或 JavaScript之类),因为你为了完成某种特定的任务,需要与一个现存的工具建立接口。不过那不是学习如何编程,而是在学习如何完成那个任务。

* 三天内: 很不幸,这不够,原因由下一节告诉我们。

在十年里学会编程

研究表明 (Hayes,Bloom) 在任何一种领域内,象下棋、作曲、绘画、钢琴演奏、游泳、网球、以及原子物理学和拓扑学,等等,要达到专家水平大约都要化十年时间。没有真正的捷径:即使是莫扎特,4岁时就是音乐神童,13年后才开始写出世界级的作品。在另一方面,披头士似乎在1964年的Ed Sullivan表演上一炮走红。但他们从1957年就开始表演,在获得大众青睐后,他们的第一个重大成功,Sgt. Peppers,是1967年发行的。Samuel Johnson (塞缪尔·约翰逊,英国辞典编纂家及作家)认为要花比十年更长的时间:“在任何领域中出类拔萃都要用毕生的劳作来取得;它不可能用较低的代价获得。” 而Chaucer(乔叟,英国诗人)感叹到:“人生短暂,学海无涯。”

这是我为编程成功开出的方子:

* 设法对编程感兴趣,并且因为它有趣而编一些程序。确保编程一直充满足够乐趣,这样你才愿意投入十年宝贵时间。

* 与其他程序员交流; 阅读其它程序。这比任何书本或训练课程都重要。

* 写程序。 最好的学习方式是 从实践中学习。 用更技术性的话说,“在一个给定的领域内,个人的最大能力不是自动地由扩展了的经验取得的,但即使是高度有经验的人也可以通过有意识的努力来提高自己的能力” (p. 366) 和 “最有效的学习需要因人而异的适当难度,目标明确的任务,丰富的信息反馈,以及重复的机会和错误修正。” (p. 20-21) 此书 Cognition in Practice: Mind,Mathematics,and Culture in Everyday Life 是阐明此观点的令人感兴趣的参考文献。

* 如果愿意,在大学里呆上4年或更长(在研究生院里)。你会接触到一些需要学历证明的工作,你会对此领域有更深的理解。如果你不喜欢学校,你可以(通过一些贡献)在工作中获得相似的经验。在任何情况下,光啃书本是不够的。Eric Raymond,The New Hacker’s Dictionary一书的作者,说过,“计算机科学不能把任何人变成编程专家,就象光研究刷子和颜料不会使人变成画家一样。” 我雇佣过的最好的程序员之一仅有高中程度;他做出了许多优秀的 软件,有他自己的新闻组,而且通过股票期权,他无疑比我富有的多。

* 和其他程序员一起做项目。在其中的一些项目中作为最好的程序员; 而在另一些项目中是最差的。当你是最好的,你能测试领导项目的能力,用你的观点激发别人。当你是最差的,你学习杰出者是怎么做的,了解他们不喜欢做什么(因为他们吩咐你做事)。

* 在其他程序员 之后接手项目。使自己理解别人写的程序。当程序的原作者不在的时候,研究什么需要理解并且修改它。思考如何设计你的程序以便后来者的维护。

* 学习至少半打的编程语言。包括一种支持类抽象的语言(象Java 或C++),一种支持函数化抽象的语言(象Lisp或ML),一种支持语法抽象的语言(象 Lisp),一种支持声明规格说明的语言(象Prolog或C++ 的模板),一种支持共行程序(coroutine)的语言(象Icon或Scheme),一种支持并行的语言(象Sisal)。

* 请记住“计算机科学”中有“计算机”一词。了解你的计算机要花多长时间执行一条指令,从内存中取一个字(有cache),从磁盘中读取连续的字,和在磁盘中找到新的位置。(答案)

* 参与一种语言标准化的工作。它可以是ANSI C++委员会,也可以是决定你周围小范围内的编程风格是应该两个还是四个空格缩进。通过任何一种方式,你了解到其他人在某种语言中的想法,他们的理解深度,甚至一些他们这样想的原因。

* 找到适当的理由尽快地从语言标准化的努力中脱身。

明白了这些,仅从书本中你能得到多少就成了一个问题。在我第一个孩子出生前,我读了所有的(关于育儿的)How to 书籍,仍然感觉是个手足无措的新手。30个月以后,我的第二个孩子快要出生了,我回头温习这些书了吗? 没有。相反,我依靠我的个人经验,它比专家写的数千页书更有用和可靠。

Fred Brooks在他的随笔 《没有银弹》 中定出了一个寻找优秀软件设计者的三步计划:

1. 尽可能早地,有系统地识别顶级的设计人员。

2. 为设计人员指派一位职业导师,负责他们技术方面的成长,仔细地为他们规划职业生涯。

3. 为成长中的设计人员提供相互交流和学习的机会。

此计划假设某些人已经具备了杰出设计者的必要才能; 要做的只是如何恰当地诱导他们。 Alan Perlis 说得更简明扼要:“每个人都能被教会雕刻:对米开朗其罗而言,反倒是告诉他哪些事不要做。同样的道理也适用于优秀的程序员。”

所以尽管买那本Java的书吧。你可能会从中学到点儿东西。但作为一个程序员,你不会在几天内或24小时内,哪怕是几个月内改变你的人生,或你实际的水平。

参考文献
Bloom, Benjamin (ed.) Developing Talent in Young People, Ballantine
, 1985.
Brooks, Fred, No Silver Bullets, IEEE Computer, vol. 20, no. 4, 1987, p. 10-19.
Hayes, John R., Complete
Problem Solver Lawrence Erlbaum, 1989.
Lave, Jean, Cognition in Practice: Mind, Mathematics, and Culture in Everyday Life, Cambridge University Press, 1988. 2001年夏天典型的1GHz PC的各种操作要花的时间

答案
各种操作的计时,2001年夏天在一台典型的1GHz PC上完成:
    执行单条指令            1 纳秒 = (1/1,000,000,000) 秒
    从L1缓存中取一个word        2 纳秒
    从主内存中取一个word        10 纳秒
    从连续的磁盘位置中取一个word    200 纳秒
    从新的磁盘位置中取一个word(寻址) 8,000,000纳秒 = 8毫秒

不少人问我,他们首先该学哪种编程语言。没有绝对的答案,不过请考虑以下几点:

* 用你的朋友的。当被问起“我该用哪种操作系统,Windows,Unix,还是Mac?”,我总是回答:“你朋友用什么,你就用什么。” 你从朋友那能学到知识,这种优势可以抵销不同操作系统或语言之间本质的差异。也考虑你将来的朋友:程序员社区 — 你将成为它的一部分如果你继续往前走的话。你选择的语言是否有一个成长中的社区,还是人数不多、即将消亡? 有没有书籍、网站、在线论坛回答你的问题?你喜欢论坛里的那些人吗?

* Keep it simple, stupid. 象C++和Java这样的语言是为经验丰富的程序员组成的团队进行专业开发而设计的,他们专注于代码运行时的效率。因此,这些语言有些部分非常复杂。 而你关注的是如何编程,不需要那些复杂性。你需要的是这样的语言: 对单个的编程新手来说,它易学易记。

* 练习。你偏爱哪种学弹钢琴的方式:通常的交互式的方式,你一按下琴键就能听到音符;还是“批量”模式,你只有弹完整首曲子才能听到音符?显然,用交互模式学习弹钢琴更容易些,编程也一样。坚持用交互模式学习并使用一种语言。

有了上面的准则,我推荐的第一个编程语言是Python或Scheme。因人而异,还有其它好的选择。如果你的年纪是10岁以下,你可能更喜欢Alice。关键是你要选择并开始实践。
附录:书籍和其它资源

不少人问我,他们该从什么书籍或网页开始学起。我重申“仅从书本里学习是不够的。” 但我还是推荐:

* Scheme: Structure and Interpretation of Computer Programs (Abelson & Sussman)可能是最好的计算机科学的入门书,而且它的确把讲授编程作为理解计算机科学的一种方法。但它具有挑战性,会让许多通过其它方式可能成功的人望而却步。

* Scheme: How to Design Programs (Felleisen et al.)是关于如何用一种优美的、函数化的方式设计程序的最好的书之一。

* Python: Python Programming: An Intro to CS (Zelle)是优秀的Python入门指导。

* Python: Python.org上有许多在线指导。

* Oz: Concepts, Techniques, and Models of Computer Programming (Van Roy & Haridi) 被视为Abelson & Sussman的当代继承者。它是对编程的高层次概念的巡视。涉及的范围比Abelson & Sussman更广,同时可能更容易学习和跟进。 它用了叫做Oz的语言,不太知名,却可以作为学习其它语言的基础。

脚注
This page also available in Japanese translation thanks to Yasushi Murakawa, in Spanish translation thanks to Carlos Rueda and in German translation thanks to Stefan Ram.
T. Capey points out that the Complete Problem Solver page on Amazon now has the “Teach Yourself Bengali in 21 days” and “Teach Yourself Grammar and Style” books under the “Customers who shopped for this item also shopped for these items” section. I guess that a large portion of the people who look at that book are coming from this page.

No Comments » | Tags: | Categories: Technology 转载, 参考消息, 精彩文章
【转】grep sed awk 使用技巧

引用

Grep的常用命令语法1. 双引号引用和单引号引用
在g r e p命令中输入字符串参数时,最好将其用双引号括起来。例如:”m y s t r i n g”。这样做有两个原因,一是以防被误解为 s h e l l命令,二是可以用来查找多个单词组成的字符串,例如:”jet plane”,如果不用双引号将其括起来,那么单词 p l a n e将被误认为是一个文件,查询结果将返回”文件不存在”的错误信息。
在调用变量时,也应该使用双引号,诸如: g r e p”$ M Y VA R”文件名,如果不这样,将
没有返回结果。
在调用模式匹配时,应使用单引号.[root@mypc ]# echo `grep 123 111.txt` (#注意是反单引号)

2. 常用的g r e p选项有:
-c 只输出匹配行的计数。
-i 不区分大小写(只适用于单字符)。
-h 查询多文件时不显示文件名。
-l 查询多文件时只输出包含匹配字符的文件名。
-n 显示匹配行及行号。
-s 不显示不存在或无匹配文本的错误信息。
-v 显示不包含匹配文本的所有行。

3. 特殊的–在多个文件中进行查询
$ grep “sort”*.doc ( #在当前目录下所有. d o c文件中查找字符串”s o r t”)

$ grep “sort it” * (#或在所有文件中查询单词”sort it”)
接下来的所有示例是指在单个文件中进行查询
4. 行匹配
$ grep -c “48″ data.f
$ 4 (#g r e p返回数字4,意义是有4行包含字符串”4 8″。)
$ grep “48″ data.f (#显示包含”4 8″字符串的4行文本)

5. 显示满足匹配模式的所有行行数:
[root@mypc oid2000]# grep -n 1234 111.txt
1:1234
3:1234ab

6. 精确匹配
[root@mypc oid2000]# grep “1234>” 111.txt
1234

7. 查询空行,查询以某个条件开头或者结尾的行。
结合使用^和$可查询空行。使用- n参数显示实际行数
[root@mypc oid2000]# grep -n “^$” 111.txt (返回结果 2: #说明第二行是空行)
[root@mypc oid2000]# grep -n “^abc” 111.txt (#查询以abc开头的行)
[root@mypc oid2000]# grep -n “abc$” 111.txt (#查询以abc结尾的行)

8. 匹配特殊字符,查询有特殊含义的字符,诸如$ . ‘ ” * [] ^ | + ? ,必须在特定字符前加。
[root@mypc oid2000]# grep “.” 111.txt (#在111.txt中查询包含”.”的所有行)
[root@mypc oid2000]# grep “my.conf” 111.txt (#查询有文件名my. c o n f的行)

9. 目录的查询
[root@mypc oid2000]# ls -l |grep “^d” (#如果要查询目录列表中的目录)
[root@mypc oid2000]# ls -l |grep “^d[d]” (#在一个目录中查询不包含目录的所有文件)
[root@mypc]# ls -l |grpe “^d…..x..x” (#查询其他用户和用户组成员有可执行权限的目录集合)

Awk的常用命令语法

awk命令擅长格式化报文或从一个大的文本文件中抽取数据包,下面是该命令的基本语法
awk [-F filed-separator] “commands” input-file(s)
[ - F域分隔符]是可选的,a w k使用空格作为缺省的域分隔符,如果在要处理的文件中是以冒号作为分割域的(如passwd文件),则在处理的时候要这样指明 awk -F: command input-file(s)

1.1域和记录
a w k执行时,其浏览域标记为$ 1,$ 2 . . . $ n。这种方法称为域标识。使用$ 1 , $ 3表示参照第1和第3域,注意这里用逗号做域分隔。如果希望打印一个有 5个域的记录的所有域,不必指明 $ 1 , $ 2 , $ 3 , $ 4 , $ 5,可使用$ 0,意即所有域。

1.2保存a w k输出
$ awk ‘{print $0}’ input-files > out-files (#重定向保存输出)
$ awk ‘{print $0}’ input-files | tee out-files (#使用t e e命令,输出到文件的同时输出到屏幕)

1.3 常用的awk命令举例
[root@mypc /]# awk ‘$0 ~ /user/’ /etc/passwd (#如果某域含有user就将该行打印出来)
rpc:x:32:32:Portmapper RPC user:/:/sbin/nologin
rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin
[root@mypc /]# awk ‘/user/’ /etc/passwd (#同上)
[root@mypc /]# awk -F: ‘{if ($5 ~ /user/) print $0}’ /etc/passwd (#如第五域有user则输出该行)
rpc:x:32:32:Portmapper RPC user:/:/sbin/nologin
[root@mypc /]# ifconfig | awk ‘/inet/{print $2}’ (#从ifconfig的输出中抽取含inet的行并打印第二域)
[root@mypc /]# ifconfig | awk ‘/inet/{print $2}’ | awk -F: ‘{print $2}’ (#在上面的基础上再抽取,这个命令可以让你直接得到本机的ip地址)

Sed的常用命令语法
Sed是一个非交互性文本流编辑器。它编辑文件或标准输入导出的文本拷贝。

1.行的匹配
[root@mypc /]# sed -n ‘2p’ /etc/passwd 打印出第2行
[root@mypc /]# sed -n ‘1,3p’ /etc/passwd 打印出第1到第3行
[root@mypc /]# sed -n ‘$p’ /etc/passwd 打印出最后一行
[root@mypc /]# sed -n ‘/user/’p /etc/passwd 打印出含有user的行
rpc:x:32:32:Portmapper RPC user:/:/sbin/nologin
rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin
[root@mypc /]# sed -n ‘/$/’p /etc/passwd 打印出含有$元字符的行,$意为最后一行

2.插入文本和附加文本(插入新行)
[root@mypc /]# sed -n ‘/FTP/p’ /etc/passwd 打印出有FTP的行
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
[root@mypc /]# sed ‘/FTP/ a 456′ /etc/passwd 在含有FTP的行后面新插入一行,内容为456
[root@mypc /]# sed ‘/FTP/ i 123′ /etc/passwd在含有FTP的行前面新插入一行,内容为123
[root@mypc /]# sed ‘/FTP/ i “123″‘ /etc/passwd在含有FTP的行前面新插入一行,内容为”123″
[root@mypc /]# sed ‘5 a 123′ /etc/passwd 在第5行后插入一新行,内容为123
[root@mypc /]# sed ‘5 i “12345″‘ /etc/passwd 在第5行前插入一新行,内容为”12345″

3.删除文本
[root@mypc /]# sed ‘1d’ /etc/passwd 删除第1行
[root@mypc /]# sed ‘1,3d’ /etc/passwd 删除第1至3行
[root@mypc /]# sed ‘/user/d’ /etc/passwd 删除带有user的行

4. 替换文本,替换命令用替换模式替换指定模式,格式为:
[ a d d r e s s [,address]] s/ pattern-to-find /replacement-pattern/[g p w n]
[root@mypc /]# sed ’s/user/USER/’ /etc/passwd 将第1个user替换成USER,g表明全局替换
[root@mypc /]# sed ’s/user/USER/g’ /etc/passwd 将所有user替换成USER
[root@mypc /]# sed ’s/user/#user/’ /etc/passwd 将第1个user替换成#user,如用于屏蔽作用
[root@mypc /]# sed ’s/user//’ /etc/passwd 将第1个user替换成空
[root@mypc /]# sed ’s/user/&11111111111111/’ /etc/passwd 如果要附加或修改一个很长的字符串,可以使用( &)命令,&命令保存发现模式以便重新调用它,然后把它放在替换字符串里面,这里是把&放前面
[root@mypc /]# sed ’s/user/11111111111111&/’ /etc/passwd 这里是将&放后面

5. 快速一行命令
下面是一些一行命令集。([ ]表示空格,[ ]表示t a b键)
‘s / . $ / / g’ 删除以句点结尾行
‘-e /abcd/d’ 删除包含a b c d的行
‘s / [ ] [ ] [ ] * / [ ] / g’ 删除一个以上空格,用一个空格代替
‘s / ^ [ ] [ ] * / / g’ 删除行首空格
‘s / . [ ] [ ] * / [ ] / g’ 删除句点后跟两个或更多空格,代之以一个空格
‘/ ^ $ / d’ 删除空行
‘s / ^ . / / g’ 删除第一个字符
‘s /COL ( . . . ) / / g’ 删除紧跟C O L的后三个字母
‘s / ^ / / / g’ 从路径中删除第一个
‘s / [ ] / [ ] / / g’ 删除所有空格并用t

a b键替代
‘S / ^ [ ] / / g’ 删除行首所有t a b键
‘s / [ ] * / / g’ 删除所有t a b键
如果使用s e d对文件进行过滤,最好将问题分成几步,分步执行,且边执行边测试结果。
经验告诉我们,这是执行一个复杂任务的最有效方式。

1 Comment » | Tags:,,,, | Categories: Technology 转载, 参考消息


我的豆瓣

二维码快速链接
QR Code fuer diese Seite