【引子】人们可以有一万个理由不学习软件编程,但觉得编程难学不应该是其中的一个。我相信,编软件的能力,与读书写字一样,应该是人们的一项基本的能力。如果你对此抱有怀疑,也许可以参考一下古代社会,那时有能力读书写字的人就被称为有文化,并被高称为识文断字。如果那时候有人说,读书写字将是人们正常社会生活中的一项基本能力,会有多少人认同呢?我想编软件的能力也是同样的。与其说学习编程是一种能力的养成,莫如说是一种信心的养成。无论如何,读者都可以跟随故事中的人物一起做一尝试。编程语言学习以Delphi为例,因为其使用简单而人性化,适用于多种类型的软件开发。各种软件语言的内在相似性很高,都起源于近代数学的可计算性理论。对一门语言的研究到了一定程度之后,所有编程语言都可触类旁通。考虑到的故事性的可读性,有些语句和语法的介绍并不详细展开。有专业性的概念都在了注释中做了介绍,读者如果确对编程语有兴趣,任何一本相关的教科书或电子版的读物都可以作为参考。
整个下午顾楠处于一种莫名的喜悦中。刘天野痛快地答应帮助自己,虽然不在预料之外,但事情的发展仍比想象的简单了许多。她无法安静地工作,而是一直在想着掌握了编程之后她可以做的每一件事情。在计算机世界里,她的想法都将实现,也许她可以编一些自己常用的工具,也许自己编一些自己喜欢的小游戏,她一直对股票交易非常有兴趣,也许可以用软件来分析股票。当她想到当她对股票的独特见解已经充分实现,每一支关注的股票都将按照自己预先的计算涨跌着,所有人都会向慕她的预见,不禁笑了起来。这种亢奋的状态一直延续到回家的路上。上下班乘公交的路上,是打工族的烦恼。从拥挤的车站,挤进更加拥挤的车厢,然后是一个多小时的颠簸,每天两次,每周五天,日子似乎没有尽头般地延续着。不过今天顾楠心情很好,而且运气也不错,只过了两站,旁边的人起身下车,她就有了座位。这真是很好的运气。在一辆车内嘈杂、空气浑浊且又拥挤不堪的公交车上,还有什么是更值得追求的东西呢?顾楠扫视了一下车厢内,许多人甚至没有一个安稳的站立位置,不得不随着汽车的起停晃来晃去。相比起来,那些能看到窗外景色,有牢固扶手的乘客多少舒适一些。而象她这样有座位的乘客,大概就是这个小世界中的最高阶层了,如果不算上司机同志的话。忽然,一丝隐约的不安侵入了顾楠的思绪,她是不是对刘天野有了好感呢?
应该不会,顾楠已经有了男朋友,他叫陆迪,性情开朗幽默,他在另外一家公司做售后支持,经常去外地出差。虽然他们见面的时间不多,但在一起的时候总是非常愉快,顾楠一点也不怀疑自己和男友的感情。在这一点上,她不希望出任何问题。
刘天野呢,他完全是另外一种风格,好象很淡漠,顾楠很少看到他对什么事情表现出有热情的样子。也许是由于对许多事情了解得太多而没有新鲜感。刘天野的知识面很广,正是由于这一点让顾楠很喜欢和他聊天。应该说刘天野的这种风格顾楠也不反感,而且这样一个对许多事情无动于衷的人却一直对自己很有耐心,似乎很喜欢听她大谈自己对事情的看法,这让她感到很满足。有时连自己都觉得很幼稚的见解,经他总结,还显得真象有些深意了的样子。
“只是喜欢一起说话,这不能说明什么的。”顾楠对自己说到。她尽力不去再想这些,让思维回到即将掌握编程的喜悦中来。
-----------------------------
Edit1.Text:= '刘天野';
-----------------------------
再次运行,效果当然出来了。顾楠的眼睛发亮:“是不是我在这里写什么都可以?”“当然了,你先熟悉着,我去订晚餐来一起吃。”刘天野当然知道那些刚接触编程的人的兴奋感受。不过显然他并不想在这里陪着。等刘天野拎着两套盒饭回来,顾楠大概已经尝试过了十几句不同的话,终于认识到,无论写什么,从程序上来说都没有什么本质的区别。现在她已经对这个简单的程序兴味索然了,直催着刘天野继续。一边吃饭,刘天野一边给顾楠讲解了变量的定义与使用和几个最常用的语句与函数,以及如何看帮助,如何设断点进行调试。还特别地讲了控件tag属性的基本应用和怎样通过过程的Sender参数访问控件,因为刘天野已经设计了后面的任务。半个多小时下来,第一次接触程序的顾楠虽然兴趣十足,但多少也已经有些接受饱和了。“今天的课就到这里,”刘天野宣布:“现在有个课后作业,开发一个计算器。功能不用太复杂,先实现最基本的整数的加减乘除就可以了。”“好啊,好啊。”能够把刚刚学到的东西实践一下,她感到很高兴。“你打算怎么实现呢?”看着顾楠高兴得眼光四射,刘天野不希望她在实际编程的时候遇到太多的麻烦,所以想帮她先理理思路。
“先放上0到9,共10个按钮,”顾楠兴致勃勃的开始规划:“不,再加上加减乘除,一共14个。对了,还有‘等于’按钮,一共15个。”
“有‘清空’按钮没有?”刘天野提醒到。
“对,加上‘清空’按钮,一共是16个。再加一个显示条,就用这个编辑框就很合适。”
“你可以定义一个全程变量,把当前的数字放在里面,一开始是0,按一个数字按钮之后,先把这个变量乘以10,再……”
“哎呀,我知道,不用你说,初中数学就学过了好不好。”顾楠很不满意刘天野把这个设计思想说了出来,她自己明明已经想到了,这样一来却好象变成是得到刘天野的提示:“你不用管了,等着看我做的结果就好了。”在回家的路上,顾楠心里暗暗地下决心,她打算不仅实现整数的加减乘除,还额外把小数的运算也实现出来,只有这样才能证明自己确实已经有了很好的构思。
---------------------------------------------------
注释:
IDE:集成开发环境,现代大部分程序语言都使用这类工具进行开发。
Edit:TEdit,编辑框,常用编程控件,一般用于获取用户输入或者显示。
Text:编辑框的关键属性,代表编辑框所显示的内容。
Button:TButton,按钮,常用编程控件,一般用于接受用户指令。
OnClick:按钮的关键事件,按下按钮后的动作写在这里。
Tag属性:所有控件都具有的一个属性,可以存放一个整数。
Sender参数:所有控件事件都会传递的一个参数,代表触发此事件的控件。
---------------------------------------------------
注释:
ReadOnly属性:只读,许多可以输入的控件都有此属性,设置为真后禁止用户修改。
陆迪聪明而勤奋,他所在的公司是一家很大的公司,而他在公司的售后部门已经是骨干员工,如果不出什么问题,他很快就可以提升为项目经理,所以工作起来格外积极。虽然顾楠很想他能少出几次差,多一些时间陪她,但她是一个多少有些传统思维的女孩,她内心认可男人就是应该以工作为重的。所以顾楠对他的工作很支持。两个人在机场耽留的时候,顾楠还是忍不住跟陆迪说起来自己学习编程的事情,并告诉他等他回来时,就可以看到她自己编的扫雷了。不过果然如顾楠所料,陆迪对此很不以为然。两人很快就开始聊其他的事情。陆迪没有给予支持,虽然早在预料之中,但也让顾楠在一个人回家的路上感到些微惆怅:还不如不告诉他,真不知道当时自己是怎么想的。不过她让自己很快就忘掉这些,回到喜悦中来。顾楠很欣赏自己的这种很快就能高兴起来的能力。
第二天,下班后,在刘天野的办公室里。刘天野先给顾楠引入了几个新事物,例如TImage控件,TImageList控件,Timer控件,随机函数等,然后打开了扫雷软件。“我们可以做得和它几乎一模一样,用这个工具——”刘天野开始给顾楠介绍一个叫做ExeScope的软件。顾楠惊讶地发现,用ExeScope打开扫雷,发现居然所有的图片都展示了出来。扫雷这个游戏,顾楠大概也玩过几千次了,当她非常熟悉界面中的小图片被一个一个地展示出来时,她有种完全不同的感觉——就有点象是魔术揭秘。尤其是当刘天野教给她把自己的程序换上了扫雷的图标,更是令她高兴不已。按照刘天野的设想,顾楠先实现初级的9*9模式,在窗口中布置81个TImage控件,每个控件中显示相应的图片,通过响应TImage的鼠标事件,就可以完美地实现基本的扫雷功能。顾楠虽然觉得初级模式实在太简单了,不过她相信有了初级模式的实现,后面的自然不远。一个完全是自己开发的扫雷游戏已经在她的脑海中出现了,她现在急于开始实现它。刘天野看出了她的心思:“你先做做看,还有几个事情,下次我再给你讲。”
---------------------------------------------------
注释:
TImage控件:图片控件,常用编程控件,用于在窗口中显示一个图片。
TImageList控件:多图片控件,常用编程控件,用于管理多个大小相同的图片。
随机函数:产生一个随机数的函数,在游戏软件中是常用函数,例如扫雷中布置雷的位置。
TTimer控件:计时器控件,常用编程控件,用于定时产生事件,可以用于计时。
ExeScope:软件工具,用来提取或修改EXE软件的图片、图标等资源。
鼠标事件:一般包括鼠标按钮的按下、抬起和鼠标移动三个事件。
---------------------------------------------------
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:私有,面向对象编程中的基本概念之一,在此区域定义的属性和方法,外界不能访问。
创建事件:窗口事件,当窗口创建时触发,可以在这里创建其他对象。
销毁事件:窗口事件,当窗口销毁时触发,可以在这里销毁其他对象。
---------------------------------------------------
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的文件,一般用于程序存储一些设置信息。
递归:函数自己调用自己。
“我们来考虑扩展的问题。”刘天野注意到顾楠的代码组织结构仍然和最开始他给的一样。除了主窗口单元外,只有一个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点了。“时间还早,我们出去走一走好么?”刘天野建议到。“啊……”顾楠有些意外,有心拒绝,但又觉得不有些合适。毕竟一起散步也不是什么过分的事情,赶紧表现出高兴地说:“好啊。”“我送你到车站。”刘天野看出了顾楠的犹豫,出了大门没走几步就不动声色地修改了散步的内涵。两人边走边聊,忽然顾楠的手机响了,是陆迪。
---------------------------------------------------
注释:
动态数组:数组的大小不是事先写定,而是可以在程序运行过程中改变的数组。
菜单:常用孔件,给窗口建立一个菜单。
真希望是看到用VC。。
扯淡!难道要人类以后说话、读书、写字都用Delphi来实现?
http://blog.sina.com.cn/yingyisoft