1、6.1 选择结构的程序设计 6.2 循环结构的程序设计,第 6 章选择结构和循环结构的程序设计,返回主目录,第6章 选择结构和循环结构的程序设计,6.1选择结构的程序设计 6.1.1条件语句1. 条件语句的格式格式一: 格式二: IF 条件IF 条件条件为真时执行的程序段条件为真时执行的程序段 ENDIFELSE继续执行ENDIF后的语句条件为假时执行的程序段,ENDIF继续执行ENDIF后的语句2. 程序执行的逻辑流程图采用选择结构的程序设计之后, 程序执行的逻辑流程将会变得复杂。为了使程序设计流程清晰、直观,经常需要在程序设计之前,先画出实际业务处理的流程图,即程序框图。约定用矩形框表示某
2、种处理的语句或程序段,它有一个入口一个出口; 用菱形框表示对条件的判断,它有一个入口两个出口。 据此约定, 图6.1、 图6.2分别是条件语句格式一和格式二的程序框图。 ,3. 对条件语句的说明(1) IF 条件、 ELSE、 ENDIF 各独占一行, 以回车符作为行结束符; (2) IF、 ELSE 和 ENDIF必须对应出现在程序中; (3) 条件语句中的条件表达式, 可以是比较运算、 逻辑运算表达式或逻辑常量。 4. 条件语句的应用举例例 6.1从键盘输入一个数值型常量存入变量x中,如果x的值大于100,则显示“大于100”,否则显示“小于100”。程序如下: CLEARINPUT 请输
3、入一个数值型数据: TO xIF x100,INPUT 请输入一个数值型数据: TO xIF x100WAIT 大于100!按任意键返回ELSEWAIT 小于100!按任意键返回ENDIFRETU例 6.2 函数(x) 0 x0,程序如下: CLEARINPUT 请输入一个数值型数据: TO xIF x0? 0ENDIFIF x=0? 1,ENDIFIF x0? 2ENDIFRETU执行上面的程序时, 如果执行INPUT语句时向变量x输入一个负数,则屏幕将显示。但是,在函数已经有了一个确定的值以后,计算机还要继续进行是否等于及是否大于的判断, 这显然是不必要的。为了使程序在得到函数的一个确定值
4、之后,不再进行不必要的判断,程序应当做一些必要的修改。,这样的程序是由三个并列的条件语句构成的。实际上, 若干个条件语句并列时,最后那个条件语句往往是不必要的。 例如, 在上面的程序中,如果输入的数据不符合前面两个条件语句的条件时,所输入的数据肯定是大于的, 可以直接输出。 这说明最后要执行的输出语句是隐含了满足复合条件: NOT X0 AND NOT X=0所以, 上面的程序可以改写成: CLEARINPUT 请输入一个数值型数据: TO xIF x0,? 0RETU &因为确定了一个函数值, 所以终止程序的执行 ENDIFIF x=0? 1RETU &因为确定了一个函数值, 所以终止程序的
5、执行ENDIF? 2 &在前面没有发生终止程序执行的条件下,才能执行这条语句RETU,可见, 同一个实际问题可以编写出不同的处理程序,在诸多个不同的处理程序中,存在一个比较优化的程序。优化的程序是语句少、 执行速度快、 逻辑严谨的程序。 例 6.3旅客托运m千克的行李, 其收费标准是:如果m小于或等于20千克,则每千克运费为1.20元, 否则超过20千克的那部分重量, 按每千克1.50元收费。 请设计程序,要求输入重量后经过计算,输出应收的运费。 程序方案一(用两个并列的IFENDIF语句): CLEARINPUT 请输入行李重量: TO m,IF m20? m? 千克的行李应交运费(超过20
6、千克的重量按1.50元每千克): ? 20*1.20+(m-20)*1.50ENDIF,RETU程序方案二(用一个IFELSEENDIF语句): CLEARINPUT 请输入行李重量: TO mIF m=20? m? 千克的行李应交运费(按1.20元每千克): ? m*1.20ELSE? m,? 千克的行李应交运费(超过20千克的重量按1.50元每千克): ? 20*1.20+(m-20)*1.50ENDIFRETU可见: (1) 一个IFELSEENDIF语句可以用两个并列的IFENDIF语句替代; ,(2) 连续若干个并列的条件语句中的条件之间不应当有交集; (3) 一个条件语句中的条件,
7、 若改用其对立条件, 也可以编写出同一问题的程序。 但是, 应选用简单、 直观的条件表达方式。 例 6.4从数据库Rsh.dbf中查找姓名为李磊的职工, 如果找不到, 显示“没找到!”并结束运行; 如果找到了, 判断该职工的工资是否小于400元, 如果小于400元, 则增加50元工资,否则显示“他的工资不小于400元”后结束。 ,在编写满足上述要求的程序之前, 应当想到所设计的程序要执行的步骤: (1) 打开作为操作对象的数据库; (2) 为了能快速查找姓名为“李磊”的职工, 数据库应当按姓名字段索引; (3) 执行快速查找“李磊”的命令; (4) 判断是否找到“李磊”的记录; (5) 如果没
8、有找到, 显示“没找到!”并退出程序; (6) 如果找到, 进一步判断其工资是否小于400元; (7) 如果工资小于400元, 李磊的工资增加50元并结束程序, 否则显示“他的工资不小于400元”并结束程序。 ,实际上, 下面的程序就是把上面的自然语言改写成了程序语言: SET EXACT OFFUSE D:rshINDEX ON 姓名 TO D:inxmFIND 李磊IF EOF()WAIT WINDOWS 没找到!按任意键退出RETU,ENDIFDISP & 显示增加工资前的记录IF 工资400REPLACE 工资 WITH 工资+50DISP & 显示增加工资后的记录ELSEWAIT W
9、INDOWS 他的工资不小于400元ENDIFRETU,ENDIFDISP & 显示增加工资前的记录IF 工资400REPLACE 工资 WITH 工资+50DISP & 显示增加工资后的记录ELSEWAIT WINDOWS 他的工资不小于400元ENDIFRETU,5. 条件语句的嵌套在满足给定条件或不满足给定条件分别执行的程序段中, 如果包含了条件语句,则称此语句为条件语句的嵌套。实际上, 多个连续的并列条件语句,可以改为嵌套的条件语句; 嵌套的条件语句也可以分解成多个并列的条件语句。 例 6.5对于给定的一个公元年号n, 满足下列条件之一时, 就是闰年: (1) 该年号n不能被100整除
10、, 但是能被整除; (2) 该年号n能被400整除。 下面是采用了条件语句嵌套设计的程序:,INPUT 请输入一个公元年号: TO nhIF nh%100=0IF nh%400 &开始n能被100整除时的处理? nh? 是闰年RETUELSE? nh? 不是闰年RETU,ENDIFELSE &下面是n不能被100整除时的处理IF nh%4=0? nh? 是闰年RETUELSE? nh,? 不是闰年RETUENDIFENDIFRETU使用条件语句嵌套时, 一定要注意IF、 ELSE、 ENDIF三者之间的对应关系, 即: ENDIF 和上面最近的IF对应; ELSE和上、 下最近的IF、 END
11、IF对应。为了从程序书写格式上能清楚地反映出这种对应关系, 使用条件语句的源程序输入时应当错落有致。,例 6.6 输入学生百分制的分数(), 输出其对应的等级制的等级()。 百分制分数和等级的对应关系为: 0,60) 不及格60,70) 及格70,90) 良好 90,100 优秀其程序如下: CLEARINPUT 请输入百分制的分数: TO fIF f60,? 不及格! & 0, 60)ELSEIF f70? 及格! & 60, 70)ELSEIF f90? 良好! & 70, 90),ELSE? 优秀! & 90, 100ENDIFENDIFENDIFRETU,编写条件语句嵌套的程序时, 注
12、意外层条件语句中的条件是内层条件语句条件的前提条件。 例如, 程序中如果最外层条件 f=60 and f0。所以,一定要完整地理解后续条件语句中的条件表达式。 判断一个百分制的分数落入连续区间中的哪一个区间, 类似这样的问题往往是从第一个区间开始判断, 若不属于第一个区间, 是否属于第二个区间, 若不属于第二个区间, 是否属于第三个区间这类问题只有巧妙的使用条件表达式,才能形成条件语句有规律的嵌套,使程序编写的逻辑清晰、简捷明了。 如果这个问题用并列的条件语句来实现, 程序就很直观了:,CLEARINPUT 请输入百分制的分数: TO FIF F=60 AND F=70 AND F90? 良好
13、! & F70,90),ENDIFIF P=90? 优秀! & F90,100ENDIFRETU当已经判断出变量F属于哪一个区间之后, 为了避免继续执行其它没有意义的判断, 最好在每个条件成立时,要执行的程序段后面, 都加一条RETURN语句。 ,6.1.2分情况处理语句1. 分情况处理语句的格式DO CASECASE 条件表达式程序段CASE 条件表达式程序段CASE 条件表达式程序段,OTHERWISE程序段ENDCASE分情况处理语句也是实现按不同情况进行不同处理的语句, 和条件语句相比,使用分情况处理语句,可以使程序的处理流程更加清晰。 为便于讲解分情况处理语句, 把例6.6的问题,
14、改用分情况处理语句编写程序如下: CLEARINPUT 请输入百分制的分数: TO fJ=DO CASE,CASE f=60 AND f=70 AND f=90j=优秀!ENDCASE? f,对应的等级是: +jRETU,上面使用了分情况处理语句的程序中,对于内存变量f的分数值, 在DO CASE和ENDCASE之间,总有一个CASE条件为真, 把对应的等级存放到内存变量j后,转去执行ENDCASE后面的语句, 即输出内存变量j中所存放的等级信息。 2. 分情况语句的动态执行过程当程序执行到分情况处理的入口语句 DO CASE时, 检测每个CASE分句中条件表达式的逻辑值,计算机将执行使第一个
15、条件表达式的逻辑值为真值的程序段,然后越过其它CASE分句, 转去执行ENDCASE之后的语句。当没有任何CASE分句中的逻辑值为真值时, 如果选择了OTHERWISE分句, 则执行OTHERWISE分句的程序段; 如果没有选择OTHERWISE分句, 则不能执行分情况处理语句中的任何程序段,直接转去执行ENDCASE后面的语句。 ,例 6.7对于人事档案数据库(Rsh.dbf), 请按屏幕上显示的下列功能进行操作: 1. 显示性别为女的记录2. 显示性别为男且工资小于元的记录3. 向数据库追加记录请用分情况处理语句编写程序,当用户输入一种功能的序号后,能转入对应的操作,输入序号以外的数字,
16、返回FoxPro。其程序如下: CLEARUSE D:rshTEXT &程序的三项功能用输出文本命令显示在屏幕上(模拟菜单),1.显示性别为女的记录2.显示性别为男且工资小于元的记录3.向数据库追加记录ENDTEXTINPUT 请选择对数据库的操作(键入数值序号): TO xhDO CASECASE xh=1LIST FOR 性别=女CASE xh=2,LIST FOR 性别=男 AND 工资500CASE xh=3APPENDEOTHERWISERETUENDCASERETU,.循环结构的程序设计,6.2.1条件循环1. 条件循环语句1) 条件循环语句的一般格式DO WHILE 条件表达式语
17、句序列1LOOP语句序列2EXIT语句序列3JB)循环体(循环执行的程序段)ENDDO,2) 说明(1) 构成条件循环语句的三个部分是: DO WHILE 条件表达式循环入口语句。循环体DO WHILE 和 ENDDO之间的程序段, 它就是反复执行的部分。 ENDDO循环体终止语句。 (2) 循环体中, 视需要可以选用两个特殊的语句: LOOP、 EXIT。 它们的作用是: LOOP当循环体执行到LOOP语句时, 终止执行LOOP语句之后的语句,立即返回到DO WHILE 条件语句。 ,EXIT当循环体执行到EXIT语句时, 退出循环, 执行ENDDO后面的语句。 显然, LOOP和EXIT语
18、句一般都是要和条件语句结合起来使用的。 3) 条件循环的动态执行过程当程序执行到循环入口语句后,计算机首先检测循环入口语句给出的条件表达式的逻辑值是否为真(.T.)值,若是真值, 则执行一次循环体。 循环体执行一次后,又返回到循环入口语句,再次检测条件表达式逻辑值的真假,以决定是否再次执行循环体。如此反复,直至循环入口语句的条件表达式的值为假时,才退出循环,转入执行ENDDO后面的语句。 显然,当程序第一次进入循环入口语句时,条件的逻辑值一般为真,且在循环体内应当有影响条件表达式运算结果的语句, 否则会出现“死循环”。,2. 条件循环程序设计例例 6.8计算的自然数之和。 分析:如果用变量n不
19、断增的方法自动产生一个个自然数,用变量s保存自然数之和,则本问题实际上就是要执行次nn和ssn。 CLEARSTOR 0 TO s,n&定义并初始化程序中将要使用的变量 DO WHIL n100 &按循环体语句中的计算方法, 循环条件应为n100 n=n+1 &产生一个自然数。 注意: 当n99 时, 产生100,s=s+n &累加一个自然数ENDDO?的自然数之和是: ? sRETU思考: (1) 为什么循环条件表达式是n100,而不是n=100? 循环体执行了多少次? (2) 如果循环条件表达式改为n=100,循环体如何相应改变? ,(3) 语句 n=n+1 除了自动产生新自然数外, 还起
20、什么作用? 例 6.9计算前若干个自然数之和, 直到和数超过一万为止。 STOR 0 TO n,sDO WHILE .T. &条件表达式是逻辑常量(.T.), 可称为强迫循环 n=n+1s=s+nIF s10000EXIT,ELSELOOPENDIFENDDO? n,s思考: (1) 什么时候需要在循环入口语句的条件表达式中直接给出逻辑真值(.T.)? (2) 当循环条件给定逻辑常量.T.时, 为什么不出现死循环? (3) 通过本例题, 分析在循环体中如何利用LOOP、 EXIT语句。 ,实际上, 循环体结束语句(ENDDO)具有LOOP语句的作用, 所以本程序中的ELSE、LOOP两条命令可
21、以去掉。 如果再把循环条件改为s10000, 程序会更加简单: STOR 0 TO n,sDO WHILE s=10000n=n+1s=s+nENDDO? n,s这样改动后, 程序也能输出同样的结果。 ,例 6.10对数据库Rsh.dbf中, 凡是工资小于元的职工, 增加元。要求不用条件替换(REPLACE)语句, 而用一个循环程序实现。 分析:因为对每一条记录,都要判断其工资是否小于元,如果小于则增加50元,否则不增加。所以,这是按此办法循环处理数据库中每一条记录的问题。CLEARUSE D:rsh&刚打开的库, 记录指针指向第一条记录DO WHILE NOT EOF() &对全库做循环处理
22、时常用NOT EOF()作为循环条件,REPLACE 工资 WITH 工资+50ENDIFSKIP &数据库指针下移, 最终会使循环条件表达式值为假值ENDDORETU当需要对数据库中的每一条记录逐一作同样的处理时, 其程序设计已公式化:USE 数据库文件名,DO WHILE NOT EOF()对当前记录处理的程序段SKIPENDDO,6.2.2计数循环1. 计数循环语句 1) 计数循环语句的一般格式FOR 内存变量=数值表达式1 TO 数值表达式2STEP 数值表达式3语句序列1LOOP语句序列2EXIT,语句序列3JB)循环执行的程序段ENDFOR | NEXTFOR语句中的内存变量也叫做
23、循环变量, 数值表达式1叫做循环变量的初值, 数值表达式 2叫做循环命令的终值, 数值表达式3叫做循环变量的步长。 2) 说明(1) FOR循环入口语句。 (2) ENDFOR或NEXT循环体结束语句。 (3) STEP 数值表达式3循环变量的步长, 可以缺省。 如果缺省, 则步长是1; 如果不缺省,步长可以是正值或负值。,如果是正值, 循环变量的初值应当小于终值, 循环体才能被执行; 如果是负值, 循环变量的初值大于终值, 循环才能执行。 (4) LOOP、 EXIT的作用同条件循环。 3) 计数循环的动态执行过程当第一次执行到 FOR 语句时, 循环变量取得数值表达式1的值, 根据数值表达
24、式3的正、 负值, 判断是否达到了数值表达式2的值。 如果已经达到,则不执行循环体语句,立即转去执行ENDFOR后面的语句;如果没有达到数值表达式2的值,执行一次循环体语句。,当第一次执行完循环体语句之后, 循环变量的当前值加上步长数值表达式3后, 再次判断循环变量的当前值是否达到了终值, 以决定是否再次执行循环体语句。 如此循环往复,直至循环变量的当前值达到终值,才去执行ENDFOR后面的语句。 2. 计数循环程序设计例例 6.11求1100的自然数倒数之和。 分析: 本题程序和求前100个自然数之和基本相同, 无非是累加自然数的倒数。,CLEARSTOR 0 TO sFOR n=1 TO
25、100s=s+1/nENDFOR?前个自然数的倒数之和是: , sRETU思考: (1) 前个自然数的倒数是如何产生的? 在今后的计数循环程序设计中,应尽可能利用循环变量的当前值参与运算;,(2) 本程序中, 如果在循环入口语句中加入步长: STEP -1,那么循环变量的初值和终值各应当是什么? (3) 如果不利用循环变量参加运算, 程序应如何设计? 例 6.12输出斐波那契数列的前项, 要求在屏幕上一行输出项。 分析: 所谓斐波那契数列,就是指数列的前两项是1、 2, 以后各项是相邻的前两项之和。例如, 在程序中,设、表示前两项数据, 则后两项依次是: aab、 bba。 ,CLEARa=1
26、&把第一项数据赋给变量ab=2 &把第二项数据赋给变量bFOR k=1 TO 10 &省略了STEP 1? a &第一次循环时输出第一项,以后输出新的奇数项? b &第一次循环时输出第二项,以后输出新的偶数项a=a+b &产生新奇数项,b=b+a &产生新偶数项IF k%2=0 &是否已经输出了项? &如是, 换行ENDIFENDFORRETU本程序执行后的输出如下:,1 2 3 4 8 13 21 34 55 89 144 233 277 610 987 1597 2584 4181 6765 10946,6.2.3 循环的嵌套如果循环体中又有循环语句, 则称为循环嵌套。 循环嵌套程序的执行
27、规律是, 外层循环每执行一次, 内层循环从头至尾执行一遍。 例 6.13打印乘法口诀。 要求每个数和从到该数本身乘积打印在同一行。 分析: 乘法口诀是有规律的,即从19中的每一个数, 都要和从到该数本身中的各数相乘一遍。所以,它是一个双重循环问题, 其外层循环从到, 其内层循环从到该数本身。在内层循环体中打印若干个表示乘法运算的式子。 ,CLEARFOR m=1 TO 9&乘数从到FOR n=1 TO m &被乘数从到m(本身)s=m*n &两数相乘 ?STR(m,1)+*+STR(n,1)+=+STR(s,2)+ &不换行输出ENDFOR? &内层循环结束时换行ENDFORRETU程序说明:
28、 ,(1) 由于一个数值常量输出时要占个字符的位置, 为了输出紧凑的“被乘数乘数乘积”的格式, 使用STR()函数, 把数值型变量存储的数值转换成字符型,并在STR()函数的参数中指定取用位字符; (2) 什么时候不换行、 什么时候换行, 是输出格式设计中往往要考虑的问题; (3) 把外层循环变量的当前值, 为内层循环变量的终值, 是十分巧妙的安排, 类似这种安排的多重循环的例子还有很多。 本程序的输出如下:,1*1= 12*1= 22*2=43*1= 33*2=63*3=94*1= 44*2=84*3=124*4=165*1= 55*2=105*3=155*4=205*5=256*1= 66
29、*2=126*3=186*4=246*5=306*6=367*1= 77*2=147*3=217*4=287*5=357*6=427*7=49 8*1= 88*2=168*3=248*4=328*5=408*6=488*7=56 8*8=649*1= 99*2=189*3=279*4=369*5=459*6=549*7=63 9*8=729*9=81,例 6.14编程要求: (1) 向数据库Rsh.dbf追加一条空记录; (2) 从键盘输入空记录各字段的对应的数据(不包括、 型字段); (3) 数据输入结束后询问用户是否修改所输入的数据, 如果修改,重新输入;如果不修改,将输入的数据写入空记录
30、中的各字段; (4) 当一个空记录写入数据库后, 询问用户是否继续输入, 如果继续输入,重复(1)(3)步操作;如果不再输入,程序结束。 CLEAR,USE D:rshDO WHILE .T.&第一层循环入口, 强制循环APPEND BLANK &追加一条空记录DO WHILE .T. &第二层循环入口, 强制循环INPUT 输入职工姓名(加界定符): TO xmINPUT 输入职工性别(加界定符): TO xbINPUT 输入出生日期(加界定符): TO csrqINPUT 输入是否已婚(加界定符): TO yh,INPUT 输入技术等级(加界定符): TO jsdjINPUT 输入职工工资
31、(数 值 型): TO gzWAIT WINDOWS 是否修改(Y/N)? TO xz1IF UPPER(xz1)=Y&把所回答的字符一律改为大写 字母CLEARLOOP &修改, 无条件返回内层循环ELSE &不修改, 将输入写入当前记录,REPLACE 姓名 WITH xm, 性别 WITH xb, 工资 WITH gz ;出生日期WITH csrq, 技术等级 WITH jsdjEXIT &写入一条记录后, 退出内层循环ENDIFENDDO &内层循环终止语句WAIT WINDOWS 是否再输入一条记录(Y/N)? TO xz2 IF UPPER(xz2)=Y &判断用户的回答CLEAR
32、LOOP &继续输入记录时无条件返回外层循环,ELSECLEARRETU &不再输入, 返回FoxPro系统ENDIFENDDO &外层循环终止语句RETU向数据库输入数据的程序设计, 一般都要使用双重循环。 内层循环可满足用户对输入数据修改的要求, 外层循环可满足用户继续输入下一条记录的要求。 程序执行过程中, 有时需要一段延时之后再向下执行, 可利用一个循环程序段实现延时。 例如:,n=0DO WHILE n10000n=n+1ENDDO例 6.15按会话方式组合成的复合查询条件, 查询人事数据库。 CLEARUSE D:rshtj=.T.&首先把逻辑常量作为字符串赋给存储查询条件的内存变
33、量ACCEPT 若复合查询条件中包含“性别”, 请输入查询表达式: TO xb,ACCEPT 若复合查询条件中包含“技术等级”, 请输入查询表达式: TO jsACCEPT 若复合查询条件中包含“工资”, 请输入查询表达式: TO gzIF xb tj=tj+xbENDIFIF js tj=tj+jsENDIFIF gz ,tj=tj+gzENDIFLOCATE FOR &tjDO WHIL NOT EOF()DISPCONTINUEENDDORETU执行该程序后, 如果对于各个ACCEPT语句的提示和输入分别为: ,若复合查询条件中包含“性别”, 请输入查询表达式: AND 性别=女若复合查
34、询条件中包含“技术等级”, 请输入查询表达式: 回车若复合查询条件中包含“工资”, 请输入查询表达式: AND 工资500则变量tj中存储的字符串应当是:.T. AND 性别=女 AND 工资500。 所以, 顺序查询语句LOCATE FOR &tj, 实际上等价于语句: LOCATE FOR .T. AND 性别=女 AND 工资500例6.15对于如何用会话方式随机生成复合查询条件的程序设计来说很有参考价值。 这类程序设计和执行中需要注意以下两个问题: ,(1) 必须对存储复合查询条件的内存变量初始赋值字符串.T.; (2) 陆续向存储复合查询条件的内存变量追加输入的查询条件, 必须是以逻辑运算符AND或OR或NOT开始的字符串。 因为ACCEPT命令只能接收字符型数据, 所以上面的程序执行中, 对应ACCEPT命令输入的查询条件不要再加字符串界定符。 ,