【引子】人们可以有一万个理由不学习软件编程,但觉得编程难学不应该是其中的一个。我相信,编软件的能力,与读书写字一样,应该是人们的一项基本的能力。如果你对此抱有怀疑,也许可以参考一下古代社会,那时有能力读书写字的人就被称为有文化,并被高称为识文断字。如果那时候有人说,读书写字将是人们正常社会生活中的一项基本能力,会有多少人认同呢?我想编软件的能力也是同样的。与其说学习编程是一种能力的养成,莫如说是一种信心的养成。无论如何,读者都可以跟随故事中的人物一起做一尝试。编程语言学习以Delphi为例,因为其使用简单而人性化,适用于多种类型的软件开发。各种软件语言的内在相似性很高,都起源于近代数学的可计算性理论。对一门语言的研究到了一定程度之后,所有编程语言都可触类旁通。考虑到的故事性的可读性,有些语句和语法的介绍并不详细展开。有专业性的概念都在了注释中做了介绍,读者如果确对编程语有兴趣,任何一本相关的教科书或电子版的读物都可以作为参考。

解决方案 »

  1.   

    【信念】“共进午餐?”顾楠灿烂地笑着。“好啊。”对于她的邀请,刘天野从未拒绝过。顾楠是这家100人左右的小型软件公司的会计,而刘天野是刚刚空降到这里大约3个月的研发部经理。两人工作中的交点本来不多,但不知怎么的却居然很聊得来。中午的工作餐两人经常在一起吃,往往聊的都是些海阔天空的话题。“我想学编程!”这次刚一坐下,顾楠就这样说到。“好啊,我可以教你一些Excel的技巧,以后你做报表的时候也许会用到。”以老师自居,是刘天野的一贯思维方式,但这次他似乎没有理解顾楠的意思。“我不是这个意思!告诉你最擅长的是什么语言?”“Delphi。”“好,我就学Delphi!”“你学Delphi?”刘天野友善而又宽容地笑着,笑容里却分明带着十足的不以为然。“对!”刘天野的这副表情,顾楠已经是看惯了的,每次看到,她都会觉得自己显得好幼稚,赶快转换话题,但这次她却很坚决。刘天野看着顾楠坚决的表情,不由得认真起来:“为什么你想学编程?给我个理由。”“大雁在秋天为什么飞到南方去?”看来顾楠早有准备。“它们当然是有充分理由的,北方冷了,找不到食物……”“那它们为什么在春天飞回来?!”还没等刘天野说完,顾楠就接了上来。刘天野虽然知道候鸟迁徙生活的原因,但他不想让这次谈话成为一个生物课堂,于是故意装做语塞,等待顾楠继续往下说:“因为那就是它们生活的一部分!这也是我生活的一部分!”“编程是很难的,而且你也从来没学过计算机软件。”刘天野的这样说道。在顾楠看来,这就代表第一个问题的答案已经被接受。“你以前说你从来不认为编程是很难的,为什么现在告诉我很难?而且计算机软件的知识你可以教我,我一定能学会的!”确实刘天野从未认为编程有什么真实的难度,但如果让他来教导象顾楠这样一个从未有过软件开发的经验的女孩子,他还必须弄清一个关键问题:“我可以教你,不过你想学到什么程度呢?”显然顾楠没有想过这个问题,沉思了一会,她抬起眼睛说到:“学到你现在的程度。”“很好,我教你,我们明天开始。”
    整个下午顾楠处于一种莫名的喜悦中。刘天野痛快地答应帮助自己,虽然不在预料之外,但事情的发展仍比想象的简单了许多。她无法安静地工作,而是一直在想着掌握了编程之后她可以做的每一件事情。在计算机世界里,她的想法都将实现,也许她可以编一些自己常用的工具,也许自己编一些自己喜欢的小游戏,她一直对股票交易非常有兴趣,也许可以用软件来分析股票。当她想到当她对股票的独特见解已经充分实现,每一支关注的股票都将按照自己预先的计算涨跌着,所有人都会向慕她的预见,不禁笑了起来。这种亢奋的状态一直延续到回家的路上。上下班乘公交的路上,是打工族的烦恼。从拥挤的车站,挤进更加拥挤的车厢,然后是一个多小时的颠簸,每天两次,每周五天,日子似乎没有尽头般地延续着。不过今天顾楠心情很好,而且运气也不错,只过了两站,旁边的人起身下车,她就有了座位。这真是很好的运气。在一辆车内嘈杂、空气浑浊且又拥挤不堪的公交车上,还有什么是更值得追求的东西呢?顾楠扫视了一下车厢内,许多人甚至没有一个安稳的站立位置,不得不随着汽车的起停晃来晃去。相比起来,那些能看到窗外景色,有牢固扶手的乘客多少舒适一些。而象她这样有座位的乘客,大概就是这个小世界中的最高阶层了,如果不算上司机同志的话。忽然,一丝隐约的不安侵入了顾楠的思绪,她是不是对刘天野有了好感呢?
    应该不会,顾楠已经有了男朋友,他叫陆迪,性情开朗幽默,他在另外一家公司做售后支持,经常去外地出差。虽然他们见面的时间不多,但在一起的时候总是非常愉快,顾楠一点也不怀疑自己和男友的感情。在这一点上,她不希望出任何问题。
    刘天野呢,他完全是另外一种风格,好象很淡漠,顾楠很少看到他对什么事情表现出有热情的样子。也许是由于对许多事情了解得太多而没有新鲜感。刘天野的知识面很广,正是由于这一点让顾楠很喜欢和他聊天。应该说刘天野的这种风格顾楠也不反感,而且这样一个对许多事情无动于衷的人却一直对自己很有耐心,似乎很喜欢听她大谈自己对事情的看法,这让她感到很满足。有时连自己都觉得很幼稚的见解,经他总结,还显得真象有些深意了的样子。
    “只是喜欢一起说话,这不能说明什么的。”顾楠对自己说到。她尽力不去再想这些,让思维回到即将掌握编程的喜悦中来。
      

  2.   

    【基础】刘天野建议下班后晚走一个小时,以教给一些顾楠必须了解的基础,使她可以尽快开始真正的编程。这也正符合顾楠的意思,可以错开高峰时段的人潮,毕竟好运气不是天天都可以遇到的。编程这种东西,大概是欺软怕硬的。刘天野昨晚把Delphi安装文件的下载地址发到了她的邮箱。所以昨晚顾楠已在家中的计算机上成功地安装了Delphi。不过她无论如何努力摆弄,怎样也不能让这个复杂的IDE做一点有含义的事情,那么多的按钮、菜单不知道点哪一个好。而在刘天野的鼠标下,它却温顺而高效地工作了起来。顾楠不禁想到了红楼梦里探春临时替代凤姐掌管容国府的那一幕。“运行程序,就是这个绿色的三角。”鼠标一点,一个光秃秃的窗口出现了,上面什么都没有。“为了让它有点作用,我们可以放上两个控件,一个Edit,再加一个Button。”顾楠的英语很一般,但刘天野不让她使用中文版的IDE,坚持说编程使用到的英语并不多,而且如果用中文的IDE,遇到问题很难跟别人沟通。“注意看这里。”刘天野觉得顾楠没有集中足够的注意力。“哎呀,我看到了,把控件放在窗口上,用鼠标调整大小和位置,这个我早会了,很多软件都是这样的。”为了证实自己确实是会的,顾楠抢过鼠标,把这个两个控件摆了一个自己满意的位置,并且额外地把窗口的大小也调整了一下,然后按下运行按钮。一个似乎有些功能的窗口出现了:“看”。当然这个窗口的输入框里还显示着无甚含义的Edit1,而按钮无论怎么按也不会有任何反应。“下一步,你想做些什么来完善它?”刘天野认识到如果让顾楠多动动手,会有更好的效率。“我想让按钮按下后,这里显示出你的名字,而在之前它应该是空白的。”顾楠用鼠标指了一下输入框。“很好,”刘天野接过鼠标,程序一关闭,Delphi的IDE就自动弹了回来。刘天野点选输入框,然后用鼠标指了一下左边的属性编辑窗口。“看,这里是这个控件的全部属性。如果想让它显示为空白,就改这里——”刘天野边说边把Text属性改为空白。“下面就是写代码了,按钮的点击事件,OnClick就在这里。”刘天野特意通过属性编辑窗来找到按钮的点击事件,而没有使用直接双击按钮的方式。一方面可以加强顾楠对属性编辑窗的重视;一方面为了更明确事件的概念。然后刘天野在代码中写下。
    -----------------------------
    Edit1.Text:= '刘天野';
    -----------------------------
    再次运行,效果当然出来了。顾楠的眼睛发亮:“是不是我在这里写什么都可以?”“当然了,你先熟悉着,我去订晚餐来一起吃。”刘天野当然知道那些刚接触编程的人的兴奋感受。不过显然他并不想在这里陪着。等刘天野拎着两套盒饭回来,顾楠大概已经尝试过了十几句不同的话,终于认识到,无论写什么,从程序上来说都没有什么本质的区别。现在她已经对这个简单的程序兴味索然了,直催着刘天野继续。一边吃饭,刘天野一边给顾楠讲解了变量的定义与使用和几个最常用的语句与函数,以及如何看帮助,如何设断点进行调试。还特别地讲了控件tag属性的基本应用和怎样通过过程的Sender参数访问控件,因为刘天野已经设计了后面的任务。半个多小时下来,第一次接触程序的顾楠虽然兴趣十足,但多少也已经有些接受饱和了。“今天的课就到这里,”刘天野宣布:“现在有个课后作业,开发一个计算器。功能不用太复杂,先实现最基本的整数的加减乘除就可以了。”“好啊,好啊。”能够把刚刚学到的东西实践一下,她感到很高兴。“你打算怎么实现呢?”看着顾楠高兴得眼光四射,刘天野不希望她在实际编程的时候遇到太多的麻烦,所以想帮她先理理思路。
    “先放上0到9,共10个按钮,”顾楠兴致勃勃的开始规划:“不,再加上加减乘除,一共14个。对了,还有‘等于’按钮,一共15个。”
    “有‘清空’按钮没有?”刘天野提醒到。
    “对,加上‘清空’按钮,一共是16个。再加一个显示条,就用这个编辑框就很合适。”
    “你可以定义一个全程变量,把当前的数字放在里面,一开始是0,按一个数字按钮之后,先把这个变量乘以10,再……”
    “哎呀,我知道,不用你说,初中数学就学过了好不好。”顾楠很不满意刘天野把这个设计思想说了出来,她自己明明已经想到了,这样一来却好象变成是得到刘天野的提示:“你不用管了,等着看我做的结果就好了。”在回家的路上,顾楠心里暗暗地下决心,她打算不仅实现整数的加减乘除,还额外把小数的运算也实现出来,只有这样才能证明自己确实已经有了很好的构思。
    ---------------------------------------------------
    注释:
    IDE:集成开发环境,现代大部分程序语言都使用这类工具进行开发。
    Edit:TEdit,编辑框,常用编程控件,一般用于获取用户输入或者显示。
    Text:编辑框的关键属性,代表编辑框所显示的内容。
    Button:TButton,按钮,常用编程控件,一般用于接受用户指令。
    OnClick:按钮的关键事件,按下按钮后的动作写在这里。
    Tag属性:所有控件都具有的一个属性,可以存放一个整数。
    Sender参数:所有控件事件都会传递的一个参数,代表触发此事件的控件。
      

  3.   

    【初期的动力】会计工作忙的时候一般在月底和月初,而在月中,颇有几天事情不多的日子。顾楠正好专心编她的计算器。顾楠在大学里学的是财务,却对计算机软件有着特别的兴趣。毕业找工作时,虽然这家软件公司待遇一般,离家也不近,但她还是选择了这里。工作两年多来,接触的都是软件,但在遇到刘天野之前,她从来没有想过自己也可以编程序。与其说是以前没有想过,不如说是她以前没有相信过这种可能。但在经过和刘天野的许多次交谈,顾楠心里渐渐觉得,如果这个世界上有一个人相信她能编程序,那个人就是刘天野。而她自己则相信,如果刘天野这样的软件高手相信她能够学会编程序,她就真的能够学会。现在她希望多给刘天野一些信心,所以编程序格外认真。计算器已经有了很熟悉的外观,许多按钮都有了正确的响应。计算器是顾楠再熟悉不过的东西了,几乎每天都用。Windows附件里的计算器软件她也经常使用。但这为了准确弄清楚计算器的工作情况,她又认真地把计算器反复研究了好几遍,顾楠发现从开发计算器软件的角度来看计算器,思维方式是完全不同的。“顾楠,借钱出差。”刘天野直接推门走了进来。刘天野进财务室从来不敲门,顾楠看他进总经理办公室也未敲过门,而他自己的办公室则又从来不关门。“又不敲门!”顾楠抱怨了一句,算是当作问候。三个月来,顾楠和刘天野之间的谈话已经完全是熟人的样子,但说起来,刘天野这个人身上总是透着一种顾楠所并不熟悉的气息。“我一周以后回来,到时候看你的计算器。”刘天野向她眨眨眼睛:“如果遇到问题,有三个方法可以解决,一个查书,一个是查帮助,一个是上网找答案,实在没有办法还可以给我发邮件。”“恩”顾楠点点头,她的程序进行了大半,还没有遇到什么真正的问题,她觉得自己能够独立完成这个程序。在周末,顾楠和男友一起去看话剧。虽然她很想告诉男友自己正在学习编程的事情,但最后还是忍住没有说。因为她觉得男友是不会对这个事情感兴趣,还是以后再说好了。她始终让话题保持在彼此都兴致高昂的领域。一周以后。“顾楠,报销。”又是刘天野推门而入,一周的出差,看起来多少瘦了一些。“好啊,票拿来。”一看是刘天野,顾楠一下子高兴了起来,得意地招呼刘天野来看:“快来看看,看看我的计算器。”一边说,一边一张一张地开始计算起来。刘天野在一旁饶有兴趣地看着,虽然操作速度慢了许多,不过顾楠兴致很高。尤其是看到了一张¥11.2的出租车票,顾楠更加高兴了,一边按一边说:“看,我的计算器还能计算小数呢。”没想到一不小心按错了一个数字,成了11.5。“啊,错了,没关系,减回来。再减去0.3。”顾楠很高兴还能演示一下减法。好一会工夫,所有的票加好了。“我来用用看。”刘天野接过鼠标。一边测试,一边问到:“小数你是怎么实现的?”这是顾楠的得意设计,刘天野一问,她就开始介绍:“一开始,我以为和整数部分一样,但后来发现很不相同。整数后面加一个数字的时候,整个数字都变大10倍;而小数后面加一个数字,整体是不变的,只是所加的数字越来越小。所以,以小数点的按下为区分,数字的改变方式处于不同的状态。所以我设了一个变量dot,按下小数点后,变为真。”听到“状态”这个词,刘天野的微微点了点头,多少职业程序员对“状态”这个概念都缺少足够的重视,而顾楠从一开始就能注意到状态的区别,这让他感到很高兴。看到刘天野赞许的表情,顾楠更加高兴:“然后,还有一个变量fenwei,记录着小数的分位,一开始是1,然后是10,然后100,每按一个数字键就它就乘以10。然后在整体数字上加上这个数字除以fenwei……”正说到这里,顾楠瞥到刘天野正在尝试着直接向显示框里输入,这里又有一个她的创举:“看,不能从这里直接修改,我发现有个ReadOnly属性,就设置为真了。”刘天野接着测试除以0,这一点顾楠果然没有考虑,程序出错了。“还可以这样的啊,”顾楠吐了一下舌头:“这个好改。”“总的来说很不错。”刘天野肯定道:“我们编程序的时候无法预测用户会干什么,所以只能尽量考虑周全一些。你可以再考虑一些可能的极端情况。还有几个建议是,能否加上回格按钮和部分清空按钮呢?象真正的计算器那样。”“恩”,顾楠点点头。在这次展示过后,她也一下子想起了好几个自己也早想增加的功能。顾楠心里暗暗想:这次让刘天野看看,我考虑的周全不周全。
    ---------------------------------------------------
    注释:
    ReadOnly属性:只读,许多可以输入的控件都有此属性,设置为真后禁止用户修改。
      

  4.   

    【扫雷】在随后的几天里,一个个的新功能被加到这个小小计算器上,两人每天午饭时的话题基本上都是顾楠在解释某个功能的实现思路;而去饭后,两人都去顾楠的财务室看她的进展。顾楠常用的财务计算器的功能已经完全实现,现在她已经开始把Windows自带的科学计算器作为参考了。“好了,”终于有一天的午饭时,刘天野建议道:“我们来开发一个新程序。”“好啊,”顾楠立刻同意。在计算器刚刚编好的几天里,她曾经尽量找各种机会使用自己做的这个计算器,但现在她的热情也消减了。刘天野知道,在软件开发的初期,必须有足够的兴趣来支持。“开发一个小游戏好不好?”刘天野建议道:“一个猜数游戏,你来猜,计算机告诉你猜的的大了还是小了。”“太没劲儿了,小学生玩的游戏……”顾楠想了想说到:“能不能编扫雷啊?我很喜欢扫雷。”“当然可以,如果开发扫雷的话,有几个新的知识点我得给先你讲一讲。”刘天野微微笑了,迅速构思了一个比较简捷的实现方案,准备着教给顾楠。“好啊,好啊。恩,那就明天下班后开始好么?”顾楠受到了鼓舞,兴趣又上来了。按她的性格,很希望今天就开始。不过晚上她的男友陆迪出差,这一次出差又将是一个月,今天她想去送他。
    陆迪聪明而勤奋,他所在的公司是一家很大的公司,而他在公司的售后部门已经是骨干员工,如果不出什么问题,他很快就可以提升为项目经理,所以工作起来格外积极。虽然顾楠很想他能少出几次差,多一些时间陪她,但她是一个多少有些传统思维的女孩,她内心认可男人就是应该以工作为重的。所以顾楠对他的工作很支持。两个人在机场耽留的时候,顾楠还是忍不住跟陆迪说起来自己学习编程的事情,并告诉他等他回来时,就可以看到她自己编的扫雷了。不过果然如顾楠所料,陆迪对此很不以为然。两人很快就开始聊其他的事情。陆迪没有给予支持,虽然早在预料之中,但也让顾楠在一个人回家的路上感到些微惆怅:还不如不告诉他,真不知道当时自己是怎么想的。不过她让自己很快就忘掉这些,回到喜悦中来。顾楠很欣赏自己的这种很快就能高兴起来的能力。
    第二天,下班后,在刘天野的办公室里。刘天野先给顾楠引入了几个新事物,例如TImage控件,TImageList控件,Timer控件,随机函数等,然后打开了扫雷软件。“我们可以做得和它几乎一模一样,用这个工具——”刘天野开始给顾楠介绍一个叫做ExeScope的软件。顾楠惊讶地发现,用ExeScope打开扫雷,发现居然所有的图片都展示了出来。扫雷这个游戏,顾楠大概也玩过几千次了,当她非常熟悉界面中的小图片被一个一个地展示出来时,她有种完全不同的感觉——就有点象是魔术揭秘。尤其是当刘天野教给她把自己的程序换上了扫雷的图标,更是令她高兴不已。按照刘天野的设想,顾楠先实现初级的9*9模式,在窗口中布置81个TImage控件,每个控件中显示相应的图片,通过响应TImage的鼠标事件,就可以完美地实现基本的扫雷功能。顾楠虽然觉得初级模式实在太简单了,不过她相信有了初级模式的实现,后面的自然不远。一个完全是自己开发的扫雷游戏已经在她的脑海中出现了,她现在急于开始实现它。刘天野看出了她的心思:“你先做做看,还有几个事情,下次我再给你讲。”
    ---------------------------------------------------
    注释:
    TImage控件:图片控件,常用编程控件,用于在窗口中显示一个图片。
    TImageList控件:多图片控件,常用编程控件,用于管理多个大小相同的图片。
    随机函数:产生一个随机数的函数,在游戏软件中是常用函数,例如扫雷中布置雷的位置。
    TTimer控件:计时器控件,常用编程控件,用于定时产生事件,可以用于计时。
    ExeScope:软件工具,用来提取或修改EXE软件的图片、图标等资源。
    鼠标事件:一般包括鼠标按钮的按下、抬起和鼠标移动三个事件。
      

  5.   

    【第五回,一个类】扫雷确实比计算器复杂得多。画好了窗口之后,后面的进展明显慢了许多。顾楠想到了继续运用一个计算器开发时的思路。在开发计算器的时候,曾利用Tag属性中记录按钮所代表的数字。这次,她在每个Image控件的Tag中记录一个2位数,用十位代表行数,用个位代表列数。这样就可以准确知道鼠标是按在了哪个位置上。她对自己的这个设计很满意。但是哪个位置上有雷记录在哪里呢?还有这个位置是处于未被点开状态,还是已经点开状态,还是被标记为雷的状态,问号状态。这么多参数都记录在Tag上,怎么弄呢?。顾楠也考虑过,把Tag设计为一个3位数,用百位来代表这些状态信息。不过那些参数不能在画界面的时候写进去,而是随机生成,动态改变的,怎么弄呢?还有怎么根据一个位置找到周围的控件,而获知雷的数量呢?完全没有头绪。计时功能倒是有了完美的实现。一开始顾楠以为在Timer控件设置的1000毫秒时间间隔是准确的,就用它当作1秒来累计。不过她观察了一段时间之后,发现并不是象她想象的那样。和Windows自带的电子表对了一下,发现经常是操作一会之后,Timer控件的计时就会慢一些。虽然只是几秒钟,但扫雷游戏争的就是这几秒钟,这样的误差就显得太大了。她试着分析了一下原因:即使是一开始只有察觉不到的很小的误差,但这样长时间地累加起来,终究会产生很大误差。大概什么东西都是这样的。不过好在,在随意地浏览Delphi的帮助时,顾楠无意中发现了TDateTime类型和now函数,给她带来了很大启发。如果记录下游戏开始的时间,然后在每个计时器事件中取当前的时间和开始时间的差,就可以避免误差,取到准确的时间了。她很高兴地发现,TDateTime类型也可以使用减法,时间的差值是用天做单位的,再乘以24得到小时,再乘以3600就得到秒了。
    ---------------------------------------------------
    i:= round((now- a)* 24* 3600);
    ---------------------------------------------------
    顾楠经过认真的测试,发现这样非常精确。顾楠用了一个TImageList控件把从0到9的数码显示图片保存起来,在取得了精确的秒数之后,通过十位和个位的提取找到正确的图片显示出来。别说,还真有一点意思出来了。“还好,”顾楠想着:“我解决了这个问题,留下那个问题,问问刘天野也是说得过去的。”听了顾楠的成就和问题,刘天野建议再用一次下班后的时间,讲一个新问题。
    “我们再建立一个新的单元,叫做UnitMineCore。”刘天野对顾楠的计时器颇加赞赏了几句之后,开始转入新的话题:“在这里我们建立一个新的类。然后,”一边说着,一边把基本的代码写好。
    ---------------------------------------------------
    type
        TMineCore= class
        end;
    ---------------------------------------------------
    刘天野故意轻描淡写地不去解释什么叫做类,他不希望顾楠对这个概念的本身过于重视,那样反而会影响她把注意力集中在它的应用上。“……然后,我们建立一个二维数组……”
    ---------------------------------------------------
    type
        TMineCore= class
            MineField: array[1..9, 1..9] of integer;
        end;
    ---------------------------------------------------“对啦,就是这个!”顾楠一听到二维数组,立刻就知道这就是解决问题的关键了。刘天野点点头,继续讲道:“现在,我们建立一个进行随机布局的函数。”光标移动到函数定义上,按下Shift+Ctrl+C,Delphi自动创建了函数的定义体。
    ---------------------------------------------------
    type
        TMineCore= class
            MineField: array[1..9, 1..9] of integer;
            procedure RandomSet;
        end;...
    ...procedure TMineCore.RandomSet;
    beginend;
    ---------------------------------------------------
    “还可以这样啊!”顾楠感到很有意思。刘天野加快了写代码的速度,他已经感到顾楠又开始信心饱满了,他希望在顾楠喊停之前多给她看一些关键的东西。
    ---------------------------------------------------
    procedure TMineCore.RandomSet;
    var
       i, j, x, y: integer;
    begin
         for i:= 1 to 9 do begin
             for j:= 1 to 9 do begin
                 MindField[i, j]:= 0;
             end;
         end;
         for i:= 1 to 10 do begin
             x:= random(9)+1;
             y:= random(9)+1;
             MindField[x, y]:= 1;
         end;
    end;
    ---------------------------------------------------
    “好了,我明白了。”顾楠宣布道。“别急,还有几个事情……”刘天野觉得还并没有把设置这个类的真正意义说清楚。“不用了,我已经明白,后面的东西我自己全都会弄了!”顾楠的声音高了起来,她可不希望刘天野认为自己所有问题都必须依靠他的提示才能解决,在这编程件事情上,必须让刘天野对她有信心,只有这样,顾楠自己才会有信心。“好,好,”刘天野没有办法继续了:“不过你至少得知道这个类怎么用啊。”一边说,刘天野回到主程序的窗口单元中,在Uses中增加了UnitMineCore的引用,在private区定义了一个叫做mine1的变量,并在窗口的窗口的创建和销毁事件中分别写了代码。
    ---------------------------------------------------
    变量定义:
        mine1: TMineCore;
    创建代码:
        mine1:= TMineCore.Create;
    销毁代码:
        mine1.Free;
    ---------------------------------------------------
    “恩——”顾楠的声音又轻柔了起来,她忽然觉得,那样高声地对刘天野大喊是不对的。---------------------------------------------------
    注释:
    TDateTime:常用变量类型,日期时间,整数部分代表日期,小数部分代表时间。
    now函数:常用函数,用于获取当前的日期时间。
    单元:Unit,Delphi的代码组织结构的基本单位。
    类:面向对象编程中的核心基础概念,具有多态性、封装性、继承性的一种数据结构。
    数组:一种数据结构,用于存贮一组按顺序排列数字。
    二维数组:一种数据结构,用于存贮一组含有两个下标的数字,例如扫雷游戏中的每个格子的状态。
    Uses:Delphi中的概念,一个单元为了使用其他单元的代码,必须在uses中引用该单元。
    private:私有,面向对象编程中的基本概念之一,在此区域定义的属性和方法,外界不能访问。
    创建事件:窗口事件,当窗口创建时触发,可以在这里创建其他对象。
    销毁事件:窗口事件,当窗口销毁时触发,可以在这里销毁其他对象。
      

  6.   

    【第六回,阶梯】知道了二维数组的应用,顾楠顿感畅快,困扰了她好几天的几个问题都迎刃而解了。没用几天,她的扫雷游戏在大多数的情况下已经可以运行了,除了有几次,她发现正常情况下应该布置10颗雷的场地,居然只布置了9颗雷,甚至最少的一次,居然只布置了8颗雷。而布雷代码是按刘天野上次写好的思路,基本未做改动。顾楠从来没有想到过怀疑这些代码的正确性,但在事实摆在面前,她不得不仔细研究起来。首先把所有有雷的标记都清空,然后循环10次,在随机选择的横坐标和纵坐标上置1,代表设一枚雷。怎么看也不象是有什么错误的样子。忽然,灵光一闪般,顾楠发现了一个错误——坐标是随机选择的,如果选择的位置上已经布过雷了,那么,实际上就会少布一枚。顾楠很为自己的发现感到高兴,经过思考和调试,她把这段代码改成了这样:
    ---------------------------------------------------
         i:= 1;
         while i<= 10 do begin
             x:= random(9)+1;
             y:= random(9)+1;
             if MindField[x, y]= 0 then begin
                MindField[x, y]:= 1;
                i:= i+ 1;
             end;
         end;
    ---------------------------------------------------
    这一次,只有在为空的地方才会布置一颗新雷,也只有这时才会计数。一天在午饭时,顾楠略带得意地告诉了刘天野她的这个发现。刘天野不禁暗中感叹,编程确实是个平心静气的工作,当时自己着忙写出的代码毕竟不够严谨;而顾楠的进步速度确实也出乎了他的意料。“根据所点下去的位置,你怎样计算周围的雷的个数。”刘天野问道。“首先判断,如果这个位置有雷,游戏就失败了。如果没有,以这个位置为中心,横坐标从-1到1,纵坐标从-1到1,求一下和就有了。”顾楠胸有成竹地说。“如果点在边界呢?”刘天野点点头,接着问道。顾楠暗暗笑了,她早就想到刘天野会问这个问题,而且她觉得自己的解决方法颇为巧妙:“我把实际的场地设置为0到10,而真正的布雷区只是1到9,这样就留出了一圈肯定没有雷边界,用我刚才方法就可以直接计算了。”“那么,点一下,遇到没有雷的地方自动开一片,这个你怎么弄的?”刘天野觉得这里必须使用递归,但顾楠应该不知道什么叫做递归。“这个啊,我是这样做的,”顾楠觉得这里自己的实现方法多少有些乱,信心也不是那么足了:“每次点开一个位置,如果周围的雷是0,我就让程序自动把它周围的八个位置,都再点一遍。”“如果周围点开还是0呢?”刘天野追问道。“那就自动再点开,好象都是自动的,我也不知道怎么回事,但确实这样可以。”顾楠解释不清了。“很好,管用就好。”刘天野知道顾楠一定是无意中用了递归。但他现在还不想引入过多的概念上的东西。为了鼓励顾楠,午饭后,刘天野特意把她编的扫雷复制到自己的机器上,时不时玩上一局。不过还没到下午,刘天野就去找顾楠了:“顾楠,你的扫雷倒是可以玩,不过,你觉不觉得还差一个什么关键功能?”“恩,早就感觉到了,最高分不能记录,玩起来很没有成就感。”顾楠承认道。“晚上,我再教你一手。”刘天野一直想把上次没有讲完的事情再给顾楠说一说。因为这个问题的掌握和运用,对于编程者,是迈上一个关键的阶梯。正好借这个机会。“不用啦,你告诉我一个思路,我可以自己从帮助里查出来的。”没想到顾楠还拒绝了,有了几次从帮助中查出有价值的信息的经验,顾楠在编程上的信心大增。“好吧,”刘天野知道这个事情不能勉强,只好给她提示:“用Ini文件就可以解决这个问题。”
    顾楠倒并不是不想听刘天野的面授课程,实际上她很喜欢和刘天野说话。只是又到了月底,事情渐渐多了起来。每天都必须下班后加班一两个小时才能忙完工作。她不想让刘天野在旁边白等着她。而且白天她已经没有精力思考编程序的事情了,只有经过路上整理一下思路,晚上到家之后,才打开电脑,研究上个把小时。即使所用的时间不多,但没用几天,顾楠也就从帮助文件中查出了Ini的完整用法,并且实现了分数的记录。顾楠心里很高兴,这个功能完全是在她自己的努力下独立实现的。她在心里想到:现在,无论什么功能,只须告诉我它的实现思路,我就能找到具体办法把它实现出来。
    ---------------------------------------------------
    注释:
    Ini文件:一种有特定格式的,扩展名为.ini的文件,一般用于程序存储一些设置信息。
    递归:函数自己调用自己。
      

  7.   

    但觉得编程难学不应该是其中的一个。我相信,编软件的能力,与读书写字一样,应该是人们的一项基本的能力。如果你对此抱有怀疑,也许可以参考一下古代社会,那时有能力读书写字的人就被称为有文化,并被高称为识文断字。如果那时候有人说,读书写字将是人们正常社会生活中的一项基本能力,会有多少人认同呢?我想编软件的能力也是同样的。与其说学习编程是一种能力的养成,莫如说是一种信心的养成。无论如何,读者都可以跟随故事中的人物一起做一尝试。编程语言学习以Delphi为例,因为其使用简单而人性化,适用于多种类型的软件开发。各种软件语言的内在相似性很高,都起源于近代数学的可计算性理论。对一门语言的研究到了一定程度之后,所
      

  8.   

    【第八回,避风塘】这次刘天野吸取了经验,他没有一上来就给顾楠解决问题,而是决定先把该说的问题都说透。程序运行的没有问题,所以他先看了看顾楠的代码,由于他给顾楠写过几个小例子,所以顾楠的一部分代码参考了他的风格,但大部分代码都是随意而写的。有的地方这样,有的地方那样。刘天野决定先从这里说起。“你已经可以写出真正有用的程序了,”刘天野总是尽量用鼓励的话开头:“现在应该开始形成自己的代码风格。”“代码风格?”顾楠感到很新奇。不过她很喜欢风格这个词。“写程序代码如同写文章,本质上无非是文本文件。只有写得清晰,才能看得明白。如果你形成了成熟稳定的代码风格,别人看到代码,就知道是你写的程序。”刘天野尽量用能让顾楠感兴趣的方式来描述问题。“哦……”顾楠开始翻看自己的代码和刘天野的代码。确实,刘天野写的部分似乎有一种内在的规范,让人一看就觉得信心十足的样子。而顾楠自己的代码显得很凌乱,有时看起来就象是临时拼凑的。“那我以后也按你的风格写。”“呵呵,好,在没有形成你自己的风格之前,可以先参照我的。”刘天野让顾楠打开一个文件,是他自己总结的代码风格。里面写着每种语句的习惯写法,变量命名的原则,加空格的规则,对齐的方式,以及注释问题,等等。顾楠看了一遍,当然现在她提不出任何异议。在她看来,这就是一个标准规范。在刘天野的指导下把代码中的许多地方逐一改过,顾楠对这一套风格已经渐渐熟悉了起来。她看到自己的代码也开始显出了规范而有信心的样子,这一点让她很高兴。
    “我们来考虑扩展的问题。”刘天野注意到顾楠的代码组织结构仍然和最开始他给的一样。除了主窗口单元外,只有一个UnitMineCore单元。果然和他所预料的一样,顾楠并没有把所有逻辑处理代码都放到TMineCore类中,而是有许多写在了窗口的事件中。他接着说道:“你知道我为什么把这个类命名为‘TMineCore’么?”“喔,扫雷核心……”顾楠还真没想过这个问题。“想一想,如果我们把所有与扫雷有关的逻辑代码都写在这个类中,而不是象你这样写在窗口里,可扩展性是不是会好一些。”刘天野想看一看,对于这样轻微的批评,顾楠是否能接受。“你说得对,一开始我也想过那样,我们现在做。”顾楠点点头,她的注意力已经放在了解决问题的办法上,并没有把这想象为一种批评。接着,在刘天野的指导下,顾楠开始把一个个的功能从窗口事件中移动到TMineCore中。随着整理,顾楠觉得自己对扫雷的实现逻辑也认识得更加清晰了。“我明白了,一开始之所以没有把功能都提炼出来,是因为有的地方还并没有想透。”顾楠总结了道。刘天野对顾楠这种态度很满意,顾楠已经渐渐认识到了表现和实现逻辑相分离的意义:“现在,让我们引入几个新概念。由于不同级别的扫雷场地大小不同,所以我们不能再使用固定数组,改用动态数组。”“就是嘛,我就觉得应该有这么个功能。”顾楠说到。“然后,数组的尺寸都改为变量,还有雷数。同时初始化的代码也得修改。””刘天野边说,边修改定义代码和布局代码。
    ---------------------------------------------------
        TMineCore= class
            MineField: array of array of integer;
            FieldWidth, FieldHeight: integer;
            MineNum: integer;
            procedure BuildField(aMode: integer);
            procedure RandomSet;
        end;procedure TMineCore.BuildField(aMode: integer);
    var
       i, j, x, y: integer;
    begin
         if aMode=1 then begin
            FieldWidth:= 9;
            FieldHeight:= 9;
            MineNum:= 10;
         end else if aMode=2 then begin
            FieldWidth:= 16;
            FieldHeight:= 16;
            MineNum:= 40;
         end else if aMode=3 then begin
            FieldWidth:= 16;
            FieldHeight:= 40;
            MineNum:= 99;
         end;
    end;procedure TMineCore.RandomSet;
    begin
         for i:= 1 to FieldWidth do begin
             for j:= 1 to FieldHeight do begin
                 MindField[i, j]:= 0;
             end;
         end;
         i:= 1;
         while i<= MineNum do begin
             x:= random(FieldWidth)+1;
             y:= random(FieldHeight)+1;
             if MindField[x, y]= 0 then begin
                MindField[x, y]:= 1;
                i:= i+ 1;
             end;
         end;
    end;
    ---------------------------------------------------
    “明白了,这样增加一个设置函数,我可以方便地改成其他尺寸了,还可以增加自定义功能。”顾楠立刻就明白了这样写的用意。“然后是在表现上,我们也更改一下方式。以前我们用了81个TImage来显示雷场,那并不是一个正规的方法,现在我们只用一个。”刘天野说到这里,看着顾楠继续说:“你觉得这样会有什么问题。”顾楠想了想,抿嘴笑了:“一个是显示,一个是取座标,没问题,我已经有思路了。”再想一想,又说到:“这样其实比你一开始教给我的方法还更简单了。”刘天野心说,逻辑的问题你已经处理好了,回头再看界面问题当然觉得简单;如果一开始就是这个方案,只怕你就不是这个看法了。不管怎么样,你会了就行了。“下一步,我们建立一个菜单……”“菜单我会弄了。”顾楠赶紧说到。“那好,接着下一个问题——”菜单的用法不难,刘天野相信顾楠应该已经基本掌握了,所以立刻跳过:“有菜单的窗口,总得有一个‘关于’窗才好看。”“恩,对啊,对啊。”顾楠立刻又认真起来了。“还有,如果你想做一个自定义的功能,也必须建立另外一个窗口。”刘天野鼠标一点,一个新的空白窗口就出现了。“除了名字不同,这个窗口和主窗口是一样的。你想显示它的时候,这样调用——”刘天野随手放了一个菜单控件,建立了一个帮助|关于项,并写下运行代码。
    ---------------------------------------------------
    form2.ShowModal;
    ---------------------------------------------------“你做的窗口好难看啊。”顾楠有了新的能力,心中喜悦。看到‘关于’窗口一出现,顾楠立刻找到了借口,抢过鼠标自己做了起来。
    两个人讲一会,练习一会,不时再闲聊几句。看看表,已经是下午3点了。“时间还早,我们出去走一走好么?”刘天野建议到。“啊……”顾楠有些意外,有心拒绝,但又觉得不有些合适。毕竟一起散步也不是什么过分的事情,赶紧表现出高兴地说:“好啊。”“我送你到车站。”刘天野看出了顾楠的犹豫,出了大门没走几步就不动声色地修改了散步的内涵。两人边走边聊,忽然顾楠的手机响了,是陆迪。
    ---------------------------------------------------
    注释:
    动态数组:数组的大小不是事先写定,而是可以在程序运行过程中改变的数组。
    菜单:常用孔件,给窗口建立一个菜单。
      

  9.   

    Mark一下
    真希望是看到用VC。。
      

  10.   

    "我相信,编软件的能力,与读书写字一样,应该是人们的一项基本的能力。"
    扯淡!难道要人类以后说话、读书、写字都用Delphi来实现?
      

  11.   

    鄙博亦每日发布,但与本站比将滞后数日,以示对本站之重视,亦望各位玉趾移临指导。
    http://blog.sina.com.cn/yingyisoft