当前位置:首页 >> IT/计算机 >>

freePascal教程


第四单元 PASCAL 语言程序设计
在上一册教材中, 我们已经初步了解了 PASCAL 语言的三种基本结构、 程序设计的基本思想 和方法。本单元是在上册内容的基础上进一步深入学习 PASCAL 语言的数组、子程序、字符串处 理等基础知识。随着学习的深入,程序设计的题目类型越来越广泛,难度也越来越大,程序设计 者不能“完全”把求解的任务推给计算机,而应对题目作较充分的分析,用较优的算法去求解, 因此在计算机上编程解题是一项极好的实践活动,它可以训练观察能力、逻辑思维能力、形式化 描述问题能力、规划能力、动手动脑分析问题和解决问题的能力。

第一课 循环结构的程序设计
在程序设计中,经常处理一些需要重复执行某些操作的问题,这就需要循环结构程序设计, 循环结构是程序设计的三种基本结构之一,循环结构是指当某个条件满足时反复执行某些语句, 否则不执行。利用循环结构,使得我们能用少而精的程序编码,来完成大量的重复计算。 在 Pascal 语言中,实现循环程序设计的主要语句有 For 语句(计数循环) 、While 语句(当型 循环) Repeat 语句 、 (直到型循环) 上册教材已经介绍了 For 语句, 。 本节课介绍 While 语句、 Repeat 语句及多重循环结构。 一、While 语句结构 For 循环语句适用于已知次数的循环结构,而在实际的问题中,有时我们并不能确切知道循 环要执行多少次, 我们判断是否结束循环的依据是某个条件是否满足, “当??时就做??” 比如 , 或者是“一直做到??时为止” ,这种循环我们称之为条件循环。在 Pascal 中条件循环语句有两 种,分别是 While 循环语句和 Repeat 循环语句。下面分别介绍这两种语句的使用方法。 While 语句用于 “当满足某一条件时进行循环” 的情况, 因此它也被称为 “当型循环” 。While 语句的语法格式如下: While <布尔表达式> do <循环体语句>; While 循环语句的执行流程如图 1-1-1 所示。

布尔表达式 False

True

循环体

图 1-1-1 While 循环语句执行流程

While 语句使用说明: (1)当布尔表达为 true 则执行循环体,若为 false,则根本不进入循环体; (2)如果一开始布尔表达式的值就为假时,While 循环就会一次循环体都不执行; (3)相反的,如果布尔表达式永远是真的,那么循环就无法结束,从而成为“死循环” ,为 了避免这种情况的发生,在循环体中就一定要有能改变布尔表达式结果的操作; (4)循环体中的语句一般情况下是多条语句,必须使用 begin 和 end 将它们括起来形成一条 复合语句。 [例 1] 计算 S=1+3+5+??+n(n 为大于 1 的奇数)。 程序如下: program ex1_1; var odds,n,sum:integer; begin write(?input a odd data:?);readln(n); sum:=0; odds:=1; while odds<=n do begin sum:=sum+odds; odds:=odds+2; end; writeln(sum); end. [例 2] 输入若干个字符,它的终止符是“?” ,计算输入的字符中字母“a”出现的次数(包括 大小写) 。 程序如下: program ex1_2; var ch:char; i:integer; begin i:=0; read(ch);

while ch<>??? do begin if (ch=?a?) or (ch=?A?) then i:=i+1; read(ch); end; writeln(?i=?,i) end. 在执行时,每当输入的字符为“a”或“A”时,则将变量 i 原有的值加上 1 再赋给 i,相当于 使 i 在原有的基础上加 1。当输入一个“?”的时候,循环条件不成立,循环结束,输出结果。 在本程序中,输入数据“?”就是循环结束的标志,这种在程序中人为设置循环结束条件的方法 我们将它叫作结束标志法,在设计循环结构程序时经常要用到这种方法。 二、Repeat 语句结构 Repeat 语句与 While 语句正好相反,Repeat 语句用于“重复执行循环体,一直到指定的条件 为真时为止”的循环结构,它又被称为“直到型循环” 。 Repeat 语句的语法格式为: repeat 循环体语句 until 布尔表达式; Repeat 循环语句的执行流程如图 1-2-1 所示。

循环体

False

布尔表达式 True

图 1-2-1 Repeat 循环语句执行流程 Repeat 语句使用说明: 1、先执行循环体,然后判断布尔表达式的值,为 false,则继续循环,为 true 则终止循环; 2、为了使 repeat 循环重复能终止,与 while 循环一样,循环体中一定要有影响布尔表达式值 的操作,否则该循环就是一个死循环;

3、repeat 语句的特点是:先执行循环,后判断结束条件,因而至少要执行一次循环体; 4、repeat-until 是一个整体,它是一个(构造型)语句,不要误认为 repeat 是一个语句,until 是另一个语句; 5、repeat 循环体可以是若干个语句,不需用 begin 和 end。 另外需要说明的是,由于 while 循环与 repeat 语句都属于条件循环语句,因此一般可以将这 两种语句互相转换,而具体使用哪条语句时要看实际情况决定。 [例 3] 利用泰勒公式求 e 的值,直到最后一项小于 10 为止。泰勒公式如下: e=1+1/1!+1/2!+1/3!+ ??+1/n! 分析:逐步往后递推,直到最后一项小于 10-7 为止。 程序如下: program ex1-3; var e,p:real; i:longint; Begin e:=1;p:=1;i:=1; repeat p:=p/i; e:=e+p; i:=i+1 until p<1e-7; writeln(‘e=’,e); end. [例 4] 求两个正整数 m 和 n 的最大公约数。 分析:求两个正整数的最大公约数采用的辗转相除法求解。以下是辗转的算法: 分别用 m,n,r 表示被除数、除数、余数。 ①求 m/n 的余数 r; ②若 r=0,则 n 为最大公约数,若 r≠0,执行第③步; ③将 n 的值放在 m 中,将 r 的值放在 n 中; ④返回重新执行第①步。 程序如下: program ex1_4;
-7

var m,n,,r : integer; begin write('Input m,n='); readln(m,n); repeat r:=m mod n; m:=n;n:=r; until r=0; writeln('The greatest common divide is',m); end. 三、多重循环结构 前面学习的循环结构都是由单一重复语句构成,即循环体部分不再是循环语句,我们把它称 之为单重循环。事实上,当一个循环体语句的循环体中包含另一个循环语句时,就构成了多重循 环,我们也称之为循环语句的嵌套结构。 由嵌套的层数分别称之为双重循环、三重循环等,处于内部的循环称为内循环,处于外部的 循环称为外循环,在循环的嵌套结构中,读者应牢牢掌握其执行过程满足“外层循环执行一次, 内层循环执行一遍”的规律。 设计多重循环时,要特别注意内、外循环之间的关系,以及各语句安放的位置,内外循环控 制变量不得同名,多重循环的执行次数为各层循环执行次数的乘积。 [例 5] 用多重循环求 100~999 之间的所有水仙花数, 所谓水仙花数, 是该数等于它各位数字 的立方和,例如:153=1 +3 +5 。 分析:根据题意,我们可以利用循环枚举三个数位上的数字,采用三重循环来求解,由于循 环次数一定,用 for 循环最为简单,程序如下: program ex1_5; var a,b,c:integer; begin for a:=1 to 9 do for b:=0 to 9 do for c:=0 to 9 do if a*a*a+b*b*b+c*c*c=a*100+b*10+c then write(a*100+b*10+c:6);
3 3 3

readln end. [例 6] 求 1~100 之间的素数,所谓素数,就是只能被 1 和它本身整除的数。 分析:要判断一个数 i 是否为素数,只要看 i 是否能被 2 到 i-1 范围内的数整除,若只要有 一个能整除 i,则 i 不是素数,否则 i 为素数。因此求 1~100 之间的素数,只要对这个范围内的 每一个数进行判断就可以了。 程序如下: program ex1_6; const n=100; var i,k,s:integer; prime:Boolean; begin s:=0; for i:=2 to n do begin prime:=true;k:=2; while (k<i) and prime do begin if i mod k=0 then prime:=false; inc(k); end; if prime then begin write(i:5); s:=s+1; if s mod 10=0 then writeln; end; end; end. 注意:上述程序 k<i 条件实际上只要 k<=trunc(sqrt(i))就可以了,可以减少循环次数。 {满 10 个就换行} {相当于 k:=k+1} {判断 i 是否为素数} {枚举 n 个数}

[例 7] 编写程序在屏幕上打印出由*组成的三角形图形, 其中三角形的层数 n 由键盘输入, 如 当 n=3 时显示图形如下: * * * * * * * * * 分析:用两层循环进行控制,外层循环控制三角形的层数,内层循环控制每一层三角形的星 号个数。 程序如下: program ex1_7; var i,j,d:integer; begin write(?input depth:?);readln(d); for i:=1 to d do begin write(? ?:d-i+1); for j:=1 to 2*i-1 do write(?*?); writeln end; end. 学习评价 1、从键盘读入 5 个数,计算它们的和与积以及平均值。 2、输入 10 个数,求出其中的最大值和最小值。 3、Fibnaocci(斐波那契)数列的定义如下:数列的第一项为 0,第二项为 1,从第三项开始, 每一项都是其前两项之和。试编程输出 Fibnaocci 数列的前 n 项元素的值。 由键盘输入, (n 3<=n<=30) 4、编程求下式中 n 的最大值:22+42+62+?+n2<1500。 5、猴子吃枣问题。猴子摘了一堆枣,第一天吃了一半,还嫌不过瘾,又吃了一个;第二天又 吃了剩下的一半零一个;以后每天如此。到第十天,猴子一看只剩下一个了。问最初有多少个枣 子? 6、要将一张 100 元的钞票换成等值的 10 元、5 元、2 元、1 元一张的小钞票,要求每次换成 40 张小钞票,每种至少一张,编程输出所有可能的换法,程序应适当考虑减少重复次数。

第二课 枚举类型和子界类型
数据是程序设计的一个重要内容,其重要特征——数据类型,确定了该数据的取值范围以及 所能参与的运算。 Pascal 语言提供了丰富的数据类型,这些数据类型可以分为三大类:简单类型、构造类型和 指针类型。其中简单类型可以分为标准类型(整型、实型、字符型、布尔型)和自定义类型(枚 举类型、子界类型) ,构造类型可以分为数组类型、集合类型、记录类型和文件类型。这些数据类 型中除了指针类型是动态数据类型外,其他的都是静态数据类型。在这些数据类型中,除了实型 以外的简单类型都是顺序类型,所谓顺序类型就是它们的值不仅是有序的而且是有顺序号的。 枚举类型和子界类型就是本课学习的主要内容。 一、枚举类型 (一)枚举类型的定义 枚举类型是用户自定义的简单类型,适用于表示和处理一些非数值数据。例如,对于一个星 期中的每一天,一年中的四季,人的性别,交通信号灯的三种颜色等非数值数据,若用数值类型 来表示它们,比如用整数 0~6 分别表示星期日、星期一~星期六,用 1~4 分别表示春、夏、秋、 冬四季,用 0 和 1 分别表示男和女,用 1~3 分别表示红、绿和黄三种颜色,则显得很不直观,处 理起来也容易出错。人们希望能用直观的方式表示这些数据,而用枚举类型就能直观地表示和处 理这些数据。 枚举类型属于用户自定义类型,因此在程序的说明部分必须对类型进行定义,只有经过定义 后,这种类型才能被使用。类型说明的关键字是 type,类型标识符由用户自已决定,原则是以字 母开头,后面跟以字母或数字,但注意不要使用 Pascal 保留字或标准标识符。如以“daytype” 表示有关日期的数据类型名,可定义如下: type daytype=(sun,mon,tue,wed,thu,fri,sat); 可见,枚举类型定义的一般格式为: type <枚举类型标识符>=(<标识符 1>,<标识符 2>,??,<标识符 n>) ; 其中,<枚举类型标识符>给出了用户定义的枚举类型的名字,括号中的<标识符 1>,<标识符 2>,??,<标识符 n>称为枚举元素,它们构成了这种枚举类型的取值范围(又称“值域”,需 ) 要注意的是<枚举类型标识符>后跟的是等号。 定义了枚举类型后,就可以在变量说明部分定义相应的枚举类型的变量了。如: type

daytype=(sun,mon,tue,wed,thu,fri,sat); colortype=(red,yellow,blue,white); var d1,d2:daytype; c:colortype; 在类型说明部分,定义了两个枚举类型:daytype 和 colortype。daytype 的值域为 sun,mon,tue,wed,thu,fri,sat 共七个; colortype 的值域为 red,yellow,blue,white 共四个。 在 变量说明部分, 定义了变量 d1、 为枚举类型 daytype, d2 它们的取值只限于类型 daytype 的值域, 还定义了变量 c 为枚举类型 colortype,它的取值只限于类型 colortype 的值域。 初学自定义类型时应特别注意将类型名与变量名区别开来,尤其不能将类型名当作变量名使 用, 类型和变量是两个完全不同的概念。 例如, 在上面的例子中, 表示变量, d1 可对其进行运算, 而 daytype 表示类型,不可对其施行任何运算。 以上类型定义和变量说明可合并在变量说明部分,即 var d1,d2: (sun,mon,tue,wed,thu,fri,sat); c: (red,yellow,blue,white); 以这样的方法说明变量的好处是较为简洁,但由于未定义类型标识符,所定义的类型不能重 复使用。 (二)枚举类型数据的特点及应用 1、枚举元素只能是标识符 枚举元素只能是标识符,不能是数值常量、字符常量等其他任何数据类型。下列的定义是错 误的: type monthtype=(1,2,3,4,5,6,7,8,9,10,11,12); colortype=(‘red’, ‘yellow’, ‘blue’); 前者错在把数值常量作为枚举值,后者错在把字符串常量作为枚举值。还需注意的是,枚举 元素是标识符,但不能把作为枚举元素的标识符视作变量名,枚举值是不能被赋值的。 2、同一个枚举元素不能出现在两个或两个以上的枚举类型定义中 在一个程序中,需要定义多个枚举类型时,需注意同一个枚举元素不能出现在两个或两个以 上的枚举类型定义中。如下列的定义是错误的: type color1=(red,yellow,blue,white);

color2=(black,blue,green); 因为枚举元素 blue 既属于枚举类型 color1,又属于枚举类型 color2。 3、枚举类型属于顺序类型 枚举类型定义中通过列出所有值的标识符来定义一个有序集合,这些值的次序和枚举类型说 明中的标识符的次序是一致的,且序号从 0 开始。变量说明如下: var d1,d2:(sun,mon,tue,wed,thu,fri,sat); 其中枚举值 sun 的序号为 0,枚举值 mon 的序号为 1,依次类推,枚举值 sat 的序号值为 6。 同时,枚举值是可以由大小来区别的,关系式 sun<tue 的值是 true,tue>fri 的值是 false。 用序号函数可以求出枚举值的序号: ord(sun)=0 succ(sun)=mon ord(mon)=1 ord(fri)=5 pred(wed)=tue pred(sat)=fri 用 succ 和 pred 函数可以求出枚举值的后继和前趋: succ(wed)=thu 当然,枚举类型中的第一个元素没有前趋,最后一个元素没有后继。 可见,枚举值可以作为序号函数(ord) 、前趋函数(pred)和后继函数(succ)的自变量。 4、对枚举类型只能进行赋值运算和关系运算 var d1,d2:(sun,mon,tue,wed,thu,fri,sat); c:(red,yellow,blue,white); 此时,在程序中允许出现以下语句: d1:=sun; d2:=fri; c:=blue; if d2=d2 then write(‘the same day’) else write(‘the different day’); 可见,对枚举类型能进行赋值运算和关系运算。但下列的语句是错误的: ① mon:=1; ② c:=sun; ③ read(d1,d2,c); ④ write(d1,d2,c);writeln(blue); 错误分析: 语句①:把枚举值当成了变量名; 语句②:枚举值 sun 不属于枚举类型变量 c 的值域;

语句③:枚举类型的变量不能用 read 或 readln 语句进行读值; 语句④:枚举类型的变量值和枚举值不能通过 write 或 writeln 语句进行输出。 5、枚举类型的应用 Pascal 不允许直接读写枚举值,所以枚举值的输入、输出常用 case 语句间接地输入、输出。 枚举值的输入,一般先读入序号,通过 case 语句将枚举值相应地赋给枚举变量;输出时,通过 case 语句判断枚举类型变量的值,输出相应的字符串。 [例 1] 有红、橙、黄、绿、蓝五种颜色的小旗,每次取出三种不同颜色的小旗表示不同的信 号,输出所有信号的方案及方案总数。 程序如下: program ex2_1; type color=(red,orange,yellow,green,blue); var m,m1,m2,m3:color; s,p:integer; begin s:=0; for m1:=red to blue do for m2:=red to blue do if m1<>m2 then for m3:=red to blue do if (m3<>m1) and (m3<>m2) then begin s:=s+1; write(s,‘:’); for p:=1 to 3 do begin case p of 1:m:=m1; 2:m:=m2; 3:m:=m3; end; {case p}

case m of red:write(‘red’:8); orange:write(‘orange’:8); yellow:write(‘yellow’:8); green:write(‘green’:8); blue:write(‘blue’:8); end; end; writeln; end; {if} writeln(‘total:’,s); end. 运行后共输出 60 种不同的信号方案。 由于枚举类型是一种顺序类型,故枚举类型的变量可以作为循环变量。 二、子界类型 (一)子界类型的定义 在实际应用中, 有些数据的变化范围只局限在某一数据类型的某一确定的区域,Pascal 语言 中,对只取某一已经定义了的顺序类型的值的某一范围的这类问题,定义了子界这一数据类型。 例如,一年中的月份不超过 12,一月中的天数不超过 31。采用子界类型便于检查数据的合法性, 增加程序的可读性,使解决这类问题既符合常规概念,又自然清晰,更好保证了程序运行的正确 性。 子界类型定义的一般格式为: type <子界类型标识符>=<常量 1>..<常量 2> 其中常量 1 称作下界,常量 2 称作上界,且上界必须大于下界。下界和上界可以是整型、字 符型、布尔型、枚举型等顺序型的两个常量,但必须是同一种顺序类型,该顺序类型称为子界类 型的 “基类型” 一个子界类型的取值范围是其基类型的取值范围中从下界到上界的连续一段, 。 子 界类型的值域实际上就是基类型值域的一个子集。 下面的类型定义部分中定义了一个枚举类型和四个子界类型: Const n=150; Type {case m} {for p}

week=(sun, mon, tue, wed, thu, fri, sat); age=0..n; days=28..31; letter='a'..'z'; workday=mon..fri; 其中,子界类型 age 可表示人的年龄,其基类型为整型,取值范围为整数 0~150;子界类型 days 可表示一个月的天数,其基类型也是整型,取值范围为整数 28~31;子界类型 letter 表示 小写字母, 其基类型为字符类型, 取值范围为字符'a'~'z'; 子界类型 workday 可表示一个星期 的工作日,其基类型为枚举类型 week,取值范围为类型 week 的五个枚举类型元素 mon~fri,注 意作为类型 workday 的基类型,类型 week 必须在类型 workday 之前定义。 在定义子界类型时应注意,子界类型的基类型必须是顺序类型,常用整型、字符类型和枚举 类型,当用枚举类型时,该枚举类型必须先定义。特别不可用实型作为子界类型的基类型。基类 型为整型、字符类型和枚举类型的子界类型分别称为整数子界类型,字符子界类型和枚举子界类 型。 在定义了一个子界类型之后,就可说明该子界类型的变量。例如,对于如上定义的子界类型 age, days, letter 和 workday,可说明这些子界类型的变量如下: Var studentage: age; n, m: days; ch1, ch2, ch3: letter; today: workday; 其中,整数子界类型变量 studentage 只能在 0~150 范围内取值,不可超出该范围,字符子 界类型变量 ch1, ch2 和 ch3 都只能在'a'~'z'范围内取值,余者类推。 在说明子界类型变量时还可先不定义相应的子界类型,直接在变量说明部分中将类型定义与 变量说明合并起来写,例如下面的变量说明是合法的: Var ch: 'A'..'Z'; u, v: 0..9; 其中,ch 被说明为字符子界类型变量,其取值范围为大写字母字符'A'~'Z'; u 和 v 被说 明为整数子界类型变量,只能取值数字 0~9。 (二)子界类型数据的特点及应用 1、对基类型适用的各种运算,均适用于该子界类型

一个子界类型与其基类型的区别仅在于取值范围不同,前者的取值范围是后者的取值范围中 连续的一部分,除此之外,子界类型具有其基类型的所有性质。所以,子界类型的运算完全取决 于其基类型,即基类型的所有运算同样适用于其子界类型。例如,整型的运算有算术运算:+、-、 *、Div 和 Mod,以及关系运算和赋值运算,这些运算都适用于整数子界类型;而适用于字符子界 类型和枚举子界类型的运算为关系运算和赋值运算。 同样,适用于一个有序类型的函数运算也适用于以其为基类型的子界类型。例如,适用于枚 举子界类型的函数运算有: 前趋函数 Pred, 后继函数 Succ 和序号函数 Ord。 由于子界类型本身也 是顺序类型,所以序号函数 Ord 适用于任何一个子界类型,只是子界类型每个值的序号是按该值 在相应的基类型中的序号来定的。例如,下面程序: Program test; Type week=(sun, mon, tue, wed, thu, fri, sat); Var d: tue..fri; n: -1..1; c: 'A'..'Z'; Begin d:=wed; n:=1; c:='A'; Writeln(Ord(d):3, Ord(n):3, Ord(c):3); End. 的输出结果为: 3 1 65 子界类型的输入、输出方法同样与其基类型相同。例如,枚举子界类型变量的值不能用标准 过程 Read 和 Write 直接输入和输出,只可采用与枚举类型变量相同的方法间接输入和输出。 2、在同一程序中,具有相同类型的不同子界类型的数据,可以混合运算 例如,若有如下变量说明: Var n: Integer; m: 0..9; k: 5..20;

则下列赋值语句都是合法的: n:=3; m:=n; k:=n*m+1; n:=(k-m)*n; 应注意在对子界类型变量赋值时,所赋的值不要越界。 若执行下列语句: m:=10;k:=n+50; 则变量 m 的值为 10,变量 k 的值为 53,均超过 m,k 的值域,则在编译时会出错。 3、子界类型的应用 [例 2] 从键盘上输入一个字符,判断其为数字字符,字母字符还是其它字符。 程序如下: Program ex2_2; Var c: Char; Begin Readln(c); Case c Of '0'..'9': Writeln('This is a digit.'); 'A'..'Z', 'a'..'z': Writeln('This is a letter.'); Else Writeln('This is a other character.'); End; End. [例 3] 确定 1900~2000 年中某年某月的天数。 程序如下: Program ex2_3; Type year=1900..2000; month=1..12; days=28..31; Var y: year;

m: month; d: days; Begin {$R+} Write('Input year(1900-2000) and month:'); Readln(y, m); Case m Of 1,3,5,7,8,10,12:d:=31; 4,6,9,11:d:=30; 2:If (y Mod 4=0) And (y Mod 100<>0) Or (y Mod 400=0) Then d:=29 Else d:=28; End; Writeln('d=',d); End. 在该程序中定义了整数子界类型 year、month 和 days 分别表示年、月和天数,为了使系统 在运行程序时能自动检查子界类型变量 y、 和 d 的值是否越界, m 在程序中加了编译器指示{$R+}。 若在运行该程序时输入越界的年份或月份,例如输入年份为 2001,则中断程序的运行,并出现如 下出错信息: range check error 学习评价 1、设有四种水果:苹果、橘子、香蕉和菠萝,现要任取其中 3 种水果,不能重复,不计先 后顺序,请编写程序列出所有可能的取法。 2、输入年、月、日,输出该月有几天。 3、类型定义 type ren=’A’..’F’; 用 A 至 F 表示 6 个人,输出 6 个人相互握手的各种情况,统计握手的次数。

第三课 数组
在前面我们所学的变量都是独立的变量,即变量之间没有内在的联系,一个变量只能表示一 个数据。如果处理的数据个数较少,仅使用简单变量编程就够了。如果处理大批数据,而且这些 数据之间有某种内在联系,例如,一个工厂 100 名工人,要给这 100 人记入每个月的工资,并求 月平均工资,那么用简单变量处理就很不方便了。像此处所说的求平均工资的问题,需要设 100 个变量:S1,S2,┉,S100, 要写出 100 个变量名,然后在求月平均工资的表达式中进行 100 项相 加运算; 如果要处理 1000 个数据, 就要设 1000 个变量, 这种方法显得太烦琐了。 为了便于描述、 处理,我们经常用一个变量来表示一批数据,而这类变量中,最常用的是数组。 数组是程序中最经常使用的数据类型,由固定数目的相同类型的元素按一定的顺序排列而 成。 并且在 Pascal 语言中, 数组的元素个数必须事先确定。 同时, 数组元素的数据类型也必须是 固定的、惟一的。比如实型数组,数组元素都必须是实型数;字符数组,数组元素都必须是字符。 一、一维数组 (一)一维数组的定义 一维数组是指只有一个下标的数组,一般用于线性批量数据的组织。 在 Pascal 语言中定义数组有两种方法: (1)在说明部分 type 区中定义数组类型,然后再在 Var 区中说明数组。形式如下: type 数组类型名=array[下标类型] of 数组元素类型; var 数组名:数组类型名; 例如: type arr1=array[1..100] of real; var a,b:arr1; 以上说明部分的 type 区中,由用户定义了一个名为 arr1 的数组类型,在 var 部分说明了 a 和 b 为 arr1 类型的数组。 (2)直接在 var 区中定义数组 var 数组名:array[下标类型] of 数组元素类型; 对于上例中 a,b 数组的定义,我们完全可以采用以下形式:

a,b:array[1..100] of real; 说明:其中 array 和 of 是 pascal 保留字。方括号中的“下标类型”可以是任何顺序类型, 即可以是整型、布尔型、字符型、枚举类型和子界类型。下标类型不仅确定了该数组中数组元素 下标的类型,也规定了数组元素可用下标的上界、下界和该数组中元素的个数。如上例:下标类 型为子界类型,它规定了数组元素下标的下界是整数 1,上界是整数 100,该数组一共包含 100 个元素。 在保留字 of 后面的数组元素类型规定了数组中所包含的每个元素的类型, 数组元素类型 可以是任何类型。在同一个数组中,所有数组元素都具有相同类型。因此我们可以说,数组是由 固定数量的相同类型的元素组成的。 再次提请注意:类型和变量是两个不同概念,不能混淆。就数组而言,程序的执行部分使用 的不是数组类型(标识符)而是数组变量(标识符) 。 (二)一维数组的引用 当定义了一个数组,则数组中的各个元素就共用一个数组名( 即该数组变量名),它们之间 是通过下标不同以示区别的。 对数组的操作归根到底就是对数组元素的操作。 一维数组元素的引 用格式为: 数组名[下标表达式] 说明:①下标表达式值的类型, 必须与数组类型定义中下标类型完全一致,并且不允许超越 所定义的下标下界和上界。 ②数组是一个整体,数组名是一个整体的标识,要对数组进行操作,必须对其元素操作。数 组元素可以象同类型的普通变量那样作用。如:a[3]:=34;是对数组 a 中第三个下标变量赋以 34 的值。read(a[4]);是从键盘读入一个数到数组 a 第 4 个元素中去。 特殊地,如果两个数组类型一致,它们之间可以整个数组元素进行传送。如: var a,b,c:array[1..100] of integer; begin c:=a;a:=b;b:=c; end. 在上程序中,a,b,c 三个数组类型完全一致, 它们之间可以实现整数组传送,例子中,先将 a 数组所有元素的值依次传送给数组 c,同样 b 数组传给 a,数组 c 又传送给 b,上程序段实际上 实现了 a,b 两个数组所有元素的交换。 (三)一维数组的应用举例 对数组的操作其实是对所有数组元素的操作,数组元素在上下界之间是连续的,我们可以通 过逐个改变下标来逐个进行访问,而下标也可以是个变量,针对这些特点,对数组的操作往往利 用下标设计循环,以提高处理效率。

[例 1] 按照顺序读入 10 个数据,以逆序方式输出。 分析:我们可定义一个数组 a 用以存放输入的 10 个数, 然后将数组 a 内容逆序输出。 程序如下: program ex3_1; var a:array[1..10] of integer; i :integer; begin writeln(?Input 10 dates:?); for i:=1 to 10 do read(a[i]); writeln(?revers:?); for i:=10 downto 1 do write(a[i]:3); end. [例 2] 编程,从数组中找出最大的一个元素,并指出它是哪一个元素。 分析:设变量 max,先将第一个元素的值赋与 max,从第二个元素开始逐个与 max 比较,如 max 小于当前元素, 则把当前元素的值赋于 max, 并记录其下标, 然后按此方法, 直到所有元素都 与 max 比较完后,max 的值即是所有元素中的最大值。 程序如下: program ex3_2; var a:array [1..100] of integer; i,k,n,max:integer; begin write('Input n:'); readln(n); writeln('Input ',n,' datas into array:'); for i:=1 to n do read(a[i]); readln; max:=a[1]; k:=1; for i:=2 to n do if max<a[i] then begin max:=a[i]; k:=i end; writeln('Max is a[',k,']=',max) end.

[例 3] 从键盘输入若干个数,将它们按照从小到大的顺序输出。 分析:在数据处理中经常使用排序。排序的方法很多,如:选择法、冒泡法、shell 排序法、 插入法等。我们这里以选择法为例如何实现排序。 选择排序的过程如下: 先将一组数 (假定为整数) 放在 a[1],a[2],?,a[n]中。 先用 a[1]和其他各个元素进行比较, 凡比它小的进行交换,一直比到 a[n],这样,a[1]中存放的便是最小的元素。然后用 a[2]和 a[3],a[4],?,a[n]进行比较,凡比它小的进行交换,这样 a[2]中存放的便是 n 个数中的次小元 素,以此类推,直到最后。于是 a[1]~a[n]便成为一组从小到大排序的数据。 对于数组 a,第一遍扫描需进行 n-1 次比较操作,最小数置于 a[1],第二遍扫描需进行 n-2 次比较操作,次小数置于 a[2],每扫描一次,比较的范围就向后移动一个位置。当扫描到第 n-1 次时,比较数据只需一次就能完成整个排序过程。 程序如下: program ex3_3; const n=10; i,j:integer; temp:integer; begin writeln('Input ',n,' datas into array:'); for i:=1 to n do read(a[i]); writeln(‘array a:’); for i:=1 to n do write(a[i]:3);writeln; for i:=1 to n-1 do for j:=i+1 to n do if a[i]>a[j] then begin temp:=a[i];a[i]:=a[j];a[j]:=temp; {交换 a[i]、a[j]} end; writeln(‘Result’); for i:=1 to n do write(a[i]:3);writeln; {输出排序结果} end. [想一想] 如果需要将一组数据从大到小排序,程序该如何调整? {输出原始数据} {假设有 10 个数据需排序} var a:array[1..n] of integer;

[例 4] 给定含有 n(n<=100)个有序(不妨设为从小到大)整数的序列,现从键盘输入一个新 的整数 x,将 x 插入到序列中,使之仍然有序。 分析: 我们将 n 个有序序列的元素存放在一个数组 a 中, 首先需要找到元素 x 的插入位置 i, 然后从第 i+1 个元素至最后一个元素都向后移动一个位置, a[j+1]:=a[j](i+1<=j<=n),最后我 即 们将元素 x 插入到第 i 个位置即可,根据这一思想,我们得到程序如下: program ex3_4; const maxn=100; var a:array[1..maxn] of integer; n,x,i,j,k:integer; begin writeln(‘Input n:’);readln(n); for i:=1 to n do read(a[i]); writeln(‘Input x:’);read(x); if x<a[1] then k:=1; if x>a[n] then k:=n; for i:=1 to n do if(x>a[i])and(x<a[i+1]) then k:=i; for j:=n downto k+1 do a[j+1]:=a[j]; a[k+1]:=x; for i:=1 to n+1 do write(a[i],’’); end. 程序当 n<100 时运行结果完全正确,但当输入的 n 值恰好是 100 时,程序将出现错误,这是 因为程序中定义的数组 a 的下标值最多为 100,而当完成插入 x 操作后,程序中 a 数组的下标值 将会达到 n+1,即 101,超出了数组下标范围从而导致错误,为修改上述程序,只需将程序中的 maxn 值定义为 101 即可,这样可避免数组下标超界。 二、二维与多维数组 前面介绍的一维数组只有一个下标。如果在数组的定义中有两个下标就得到二维数组类型, 有多个下标类型就得到多维数组类型。同一维数组一样,每个下标都有必须为一个顺序类型的数 据类型,且每个下标都有上界和下界,一旦下标确定,数组元素的个数就确定了,且数组元素的 总个数是每个下标类型值的个数的乘积。对于二维或多维数组,其定义方式与一维数组类似,我

们同样可以在 type 说明中先定义,然后在 var 变量中进行说明,也可以直接在 var 变量中进行说 明。 二维数组的定义如下: type 数组类型名=array[<下标类型 1>,<下标类型 2>] of <元素类型> 例如:有下面二维数组定义: type arr1=array[1..3,1..5] of integer; var a,b:arr1; 我们可以把一个定义好的二维数组想象为一张二维表格,每个单元表格相当一个数组元素, 例如以上定义的 a,b 都是一个 3 行 5 列的二维数表,数表中共有 3*5=15 个数组元素,如图 3-2-1 所示,在二维数组中,元素的存放顺序是按照先行后列的方式存放的。 a[1,1] a[2,1] a[3,1] a[1,2] a[2,2] a[3,2] a[1,3] a[2,3] a[3,3] 图 3-2-1 二维数表 对于二维数组元素的访问,一般用一个循环控制一个下标的变化(行数) ,用另一个循环控 制另一个下标的变化 (列数) 通过双重循环来实现对二维数组全部或部分数组元素的操作, , 例如, 对于上面定义的二维数组 a 的输入输出的程序段如下: for i:=1 to 3 do for j:=1 to 5 do read(a[i,j]); for i:=1 to 3 do begin for j:=1 to 5 do writel(a[i,j],? ?); writeln; end; 对于多维的情况,一般用来描述一个空间状态,例如,描述空间一个点的坐标,假设该点的 空间坐标 X,Y,Z 轴的坐标为整数,假设如下定义一个三维数组来表示空间点的坐标: type arr1=array[0..9,0..9,0..9] of integer var a:arr1; {输出} {输入} a[1,4] a[2,4] a[3,4] a[1,5] a[2,5] a[3,5]

这样,我们就可以得到 1000 个空间上的点 a[x,y,z](0<=x,y,z<=9),多维数组中元素的访问 往往通过多循环来控制,由于数组的维数越多,占用的内存空间越大,因此在程序设计中一般不 超过三维数组。 [例 5] 从键盘读入 10 个学生语文、数学、英语这三门课的成绩,计算出每位学生的平均分 和每门课程 10 个学生的平均分。 分析:定义一个 10 行 3 列的二维数组来存放这些成绩,定义一个 10 个元素的一维数组来存 放学生平均分,定义一个 3 个元素的一维数组来存放学科平均分。 程序如下: program ex3_5; var a:array[1..10,1..3] of integer; b:array[1..10] of real; c:array[1..3] of real; i,j:integer; begin for i:=1 to 10 do begin for j:=1 to 3 do read(a[i,j]); readln; end; for i:=1 to 10 do b[i]:=0; for i:=1 to 10 do begin for j:=1 to 3 do b[i]+a[i,j]; b[i]:=b[i]/3; end; for i:=1 to 10 do writeln(b[i]:5:1); for i:=1 to 3 do c[i]:=0; for i:=1 to 3 do begin for j:=1 to 10 do c[i]:=c[i]+a[j,i]; c[i]:=c[i]/10

end; for i:=1 to 3 do writeln(c[i]:5:1); end. [例 6] 打印杨辉三角形的前 10 行。杨辉三角形如图 3-2-2: 1 1 1 1 1 1 5 4 3 6 10 2 3 4 10 5 1 1 1 1 1 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 10 10 5 1 图 3-2-3

图 3-2-2

分析:观察图 3-2-2,大家不容易找到规律,但是如果将它转化为图 3-2-3,不难发现杨辉 三角形其实是一个二维表的下三角部分,假设通过二维数组 yh 存储,每行首尾元素为 1,且其中 任意一个非首尾元素 yh[i]的值其实就是 yh[i-1,j-1]与 yh[i-1,j]的和, 另外每一行的元素个数 刚好等于行数。有了数组元素的值,要打印杨辉三角形,只需控制一下输出起始位置就行了。 程序如下: program ex3_6; var yh:array[1..10,1..10] of integer; i,j:integer; begin yh[1,1]:=1; for i:=2 to 10 do begin yh[i,1]:=1;yh[i,i]:=1; for j:=2 to i-1 do yh[i,j]:=yh[i-1,j-1]+yh[i-1,j]; end; writeln(‘Yang Hui:’); for i:=1 to 10 do begin write(‘ ‘:40-3*i); {设定打印起始位置} for j:=1 to i do write(yh[i,j]:6); {设定非首尾值} {设定首尾值} {设定第一行的值} {设定第 2~10 行的值} {需要打印前 10 行}

writeln; end; end. 学习评价 1、读入 10 个学生的学号和成绩,计算他们的平均分,若比平均分高 10 分的等第为 A,若 比平均分高小于 10 分的等第为 B,若低于平均分,则等第为 C,输出他们的成绩和等第。 2、从键盘读入一个以句号结尾的英文句子,统计并输出句中各字母出现的次数,假设句子 是由小写字母组成的。 3、 输入一个 m 行和 m 列的矩阵, 求它的转置矩阵, 即对所有的 i,j,将 a[i,j]与 a[j,i]交换。 4、求一个 5 X 5 数阵中的马鞍数,输出它的位置。所谓马鞍数,是指在行上最小而在列上 最大的数。如下: 5 6 7 8 9 4 5 6 7 8 3 4 5 2 1 2 3 4 9 0 1 2 5 4 8 则 1 行 1 列上的数就是马鞍数。

第四课 字符串
计算机除了具有强大的数值计算功能之外,还可以处理非数值的数据,因此,在程序设计竞 赛中,字符和字符串经常成为程序处理的对象,读者应熟练掌握有关字符和字符串的处理方法。 一、字符数组与字符串之间的关系 在定义数组时,如果数组元素的基本类型是字符型,则该数组就构成了字符型数组,同其他 数组一样,对字符数组元素的操作也是通过对下标的控制实现的,且字符数组元素同样可以进行 读、写、赋值、大小关系的比较等操作,为了使字符的处理更加方便,Pascal 语言提供了一种新 的字符串数据类型。什么是字符串呢?顾名思义,字符串就是指的是一串字符,为区别于其他标 识符,Pascal 语言规定,字符串数据与单个字符数据一样,必须用单引号括起来。事实上,我们 在前面的一些程序里,特别是在 write(writeln)过程语句中,为了输出提示字符,已不止一次地 使用过字符串常量。如: ‘Please Input a,b:’就是一个字符串数据。字符串数据也有字符串常 量和变量,字符串常量就是用单引号括起来的字符序列。 同其他变量一样,在使用字符串变量以前,我们得首先对字符串变量进行定义,字符串类型 定义的一般形式如下: type <字符串类型标识符>=string[长度]或者 string; var 字符串变量名:字符串类型名; 也可直接定义字符串变量如下: var 字符串变量名:string[长度]或者 string; 其中 string 为保留字,整型常数指明了字符串的最大长度,其值不能超过 255。当长度值及 其外括号省略时,隐含表示长度为 255。 例如,我们可以在 type 类型说明中如下说明: type name=string[40]; 然后在 var 变量说明中对变量进行说明,如: var name1,name2:name; 这样我们就定义了两个字符串变量 name1 和 name2,在程序中, 我们就可以对 name1,name2 进 行操作。 字符串变量为其中每个字符都规定了相应的下标。串中实际字符的下标从 1 到串的长度值, 即通过字符串的下标可以访问字符串中单个字符。这可以通过在字符串变量标识符后面加上用方 括号括起来的整型下标表达式来实现,也就是说字符串在很多方面和字符数组类型有相似之处。

比如,两者都靠下标引用其中的元素(字符) ,都可以被读写、赋值,然而,它们之间还存在一些 差别。其中,最主要有两个差别是:字符串中的字符个数(即字符串长度)可以从 0 到所指出的 上限间动态变化,而数组元素的个数是固定不变的;另外在内存中的存储方式不同。 二、字符串运算 字符串可以参加的运算有:连接运算、关系运算和赋值运算三种。 连接运算是用“+”符号将两个字符串连接成一个字符串,如: “Turbo”+“Pascal”的结果 为“TurboPascal”。 字符串的关系运算用于字符串的比较。字符串关系运算的结果是一个布尔值。当两个字符串 进行比较时,是从左到右逐个比较两个字符串中每个字符的 ASCII 码值,直到找到不相同字符为 止。此时 ASCII 码值在的字符所在串就大。如果两个字符长度不同,且直至较短串的最后一个字 符处两者都相同,则认为较长串值大,较短串值小。只有当两个字符串长度相等且对应字符完全 相同时,这两个字符串才被认为是相等的。总之,字符串的比较是按字典顺序进行的,排在前头 的字符值小,后面的字符值较大。另外,字符串的连接运算优先级别高于字符串的关系运算。 例如,下列表达式中: ‘ASC’=‘ASC’; ‘ASC’<‘ASCII’; ‘3’<‘24’; {结果为 true} {结果为 true} {结果为 false}

字符串的赋值操作是指将一个字符串表达式的结果赋给一个字符串变量,它和字符串的连接 运算一样,受到字符串变量的长度限制,一旦超出定义好的长度则后面的字符将被删除掉。 三、字符串标准过程和标准函数 在 Free Pascal 中提供了如下与字符串类型有关的标准过程和函数,以便于字符串数据的处 理应用。 函数名 concat(s1,s2,?,sn) 意义 连接字符串 返回字符串 s 中第 copy(s,index,count) index 位置开始的长度 为 count 的子串 length(s) 返回字符串 s 的长度 integer string 结果类型 string 示例 s1:= ‘123’;s2:= ‘456’; s:=concat(s1,s2); 结果:s=‘123456’ s:= ‘123456’; sl:=copy(s,2,3); 结果:s1=‘234’ s:= ‘123456’; l:=length(s); 结果:l=6

pos(subs,s)

返回子串 subs 在字符 串 s 中的起始位置

s:= ‘123.45’; byte i:=pos(‘.’,s); 结果:i=4

过程名 delete(s,index,count)

意义 将字符串 s 中第 index 位置开始长度 为 count 的子串删除 在串 s 中第 index 位置前插入一个指 定子串 s1 把数值x转换成相应的字符串s,可以 定义 x 的宽度和小数个数

示例 s:= ‘Turbo Pascal’; delete(s,6,7); 结果:s=‘Turbo’ s:= ‘ Pascal’; insert(‘Turbo’,s,1); 结果: ‘Turbo Pascal’ s= i:=1234; str(i,s); 结果:s=‘1234’

insert(s1,s,index)

str(x:m:n,s)

把字符串s转换成相应的数值k, code s:= ‘1234’; val(s,k,code) 为返回代码,为 0 表示轮换功,否则 val(s,k,code); 转换失败 四、字符串应用举例 [例 1] 对给定的 10 个国家名,按其字母的顺序输出。 程序如下: program ex4_1; var i,j:integer; t:string[20]; const name:array[1..10] of string[20]=(‘China’,’France’,’Canada’, ’Australia’,’Spain’,’American’,’Sweden’,’Poland’, ‘Turkey’,’Japan’); begin for i:=1 to 9 do for j:=i+1 to 10 do if name[i]>name[j] then begin t:=name[j];name[j]:=name[i];name[i]:=t end; for i:=1 to 10 do write(name[i]); 结果:k=1234

end. [例 2] 随机输入一个长度不超过 255 的字符串,使用 copy 函数 delete 过程和 insert 过程 将其倒置输出。 程序如下: program ex4_2; var s,sub:sting; k:integer; begin readln(s); for k:=2 to length(s) do begin sub:=copy(s,k,1); delete(s,k,1); insert(sub,s,1); end; writeln(s); end. 学习评价 1、输入 3 个字符串 r、s、t,把字符串 t 中所有形如 s 的子串替换成 r。 2、做一个加法器。完成 30000 以内的加法,两个加数间用“+”连接,可以连加,回车表示式 子输入完成;“#”表示结束运算,退出加法器。

第五课 函数与过程
前面我们学习了程序设计中的三种基本控制结构(顺序、选择、循环) 。用它们可组成任何 程序。但在应用中,还经常用到子程序结构。 通常,在程序设计中,我们会发现一些程序段在程序的不同地方反复出现,此时可以将这些 程序段作为相对独立的整体,用一个标识符给它起一个名字,凡是程序中出现该程序段的地方, 只要简单地写上其标识符即可。这样的程序段,我们称之为子程序。 引入子程序之后,可以方便地把较为复杂的问题分解分若干较小但易处理的子问题,更重要 的是使程序的结构清晰、层次分明,增强了程序的可读性,使程序易于调试和维护。 在一个程序中可以只有主程序而没有子程序(本章以前都是如此),但不能没有主程序,也就 是说不能单独执行子程序。pascal 中子程序有两种形式:函数和过程。 一、函数 (一)自定义函数 前面的学习中, 我们已经使用了 Pascal 提供的各种标准函数, sqrt(x), 如 abs(x), succ(x) 等等,这些函数为我们编写程序提供了很大的方便,但其种类和数量非常有限,在解决很多实际 问题时,不能满足用户的不同需求。为此,Pascal 语言允许用户根据需要自定义函数。 函数定义的一般格式如下: function 函数名(形式参数表):函数类型; {函数首部} 说明部分; begin 语句组; 函数名:=表达式 {给该函数赋返回值} end; 说明: ①函数由首部与函数体两部分组成; ②函数首部以保留字 function 开头; ③函数名由用户自定义一个标识符; ④函数的类型也就是函数值的类型,所求得的函数值通过函数名传回调用它的程序。可见, 函数的作用一般是为了求得一个值; ⑤形式参数简称形参,形参即函数的自变量。自变量的初值来源于函数调用。在函数中,形 参一般格式如下: 变量名表 1:类型标识符 1;变量名表 2:类型标识符 2;?;变量名表 n:类型标识符 n

函 数 体

⑥当缺省形参表(当然要同时省去一对括号)时,称为无参函数; ⑦函数体与程序体基本相似,由说明部分和执行部分组成; ⑧函数体中的说明部分用来对本函数使用的标号、常量、类型、变量、子程序加以说明,这 些量只在本函数内有效; ⑨函数体的执行部分由 begin 开头,end 结束,中间有若干用分号隔开的语句,只是 end 后 应跟分号,不能像程序那样用句号“.”; ⑩在函数体的执行部分,至少应该给函数名赋一次值,以使在函数执行结束后把函数值带回 调用程序。 在 Pascal 中,函数也遵循先说明后使用的规则,在程序中,函数的说明放在调用该函数的 程序(主程序或其它子程序)的说明部分。函数的结构与主程序的结构很相似。 [例 1] 编写一个求 n!的函数 fac。 程序如下: function fac(n:integer):integer; var k,t:integer; begin t:=1; for k:=2 to n do t:=t*k; fac:=t; end; [例 2] 编写一个根据三角的连长求面积的函数 area。 三角形面积公式为: s= p(p - a)(p- b)(p - c) 其中,a,b,c 为三条边长,p 为半周长,即 p=(a+b+c)/2。 程序如下: function area(a,b,c:real):real; var p:real; begin p:=(a+b+c)/2; area:=sqrt(p*(p-a)*(p-b)*(p-c)); end; {将结果赋值给函数} {函数首部} {局部变量说明}

(二)函数调用 自定义函数一经定义,我们可以在任何与函数值类型兼容的表达式中调用函数,在被调用时 函数不能独立成为一个语句,函数只能出现在表达式可以出现的地方。函数调用方式与标准函数 的调用方式相同。 函数调用的形式为: <函数名>(实在参数表) 说明: 实在参数表各个实在参数(简称实参)之间用逗号隔开,实参和函数定义中的形参必须按序 一一对应,个数相等,类型相容。实参的名字跟形参的名字无关,在程序中它们总是代表不同的 存储单元。 [例 3] 求 3!+5!+7!的值。 程序如下: program ex5_3; var s:integer; function fac(n:integer):integer; var k,t:integer; begin t:=1; for k:=2 to n do t:=t*k; fac:=t; end; begin {主程序} s:=fac(3)+fac(5)+fac(7); writeln(‘s=’,s); end. [例 4] 计算如图 5-1-1 多边形的面积。 b1 b6 b5 b7 b4 b3 b2 从图中可以看出,五边形的面积是三个三角形面积之和。 {定义求 n!的函数 fac}

图 5-1-1 程序如下: program ex5_4; var b1,b2,b3,b4,b5,b6,b7,s:real; function area(a,b,c:real):real; var p:real; begin p:=(a+b+c)/2; area:=sqrt(p*(p-a)*(p-b)*(p-c)); end; begin write(‘Please input b1,b2,b3,b4,b5,b6,b7:’); readln(b1,b2,b3,b4,b5,b6,b7); s:=area(b1,b5,b6)+area(b2,b6,b7)+area(b3,b4,b7); {三次调用函数 area} writeln(‘s=’,s:10:3); end. 二、过程 通过函数我们可以得到一个计算结果,然而有时我们希望通过一个子程序得到多个计算机结 果, 或者仅仅只想让子程序完成一项任务 (比如输出固定格式的结果) 而不需要返回任何结果时, 我们通常用过程来解决问题。我们以前用过的读语句 read、readln,写语句 write、writeln,实 际上都是过程语句,由于他们由 Pascal 语言系统预先说明的,故称为标准过程。 (一)自定义过程 Pascal 语言中的自定义过程与自定义函数类似,都需要在程序中先定义后使用。 过程定义的一般格式为: procedure 过程名[(形式参数表)]; 说明部分; begin 语句组; end; 说明: ①过程由过程首部与过程体两部分组成; {过程首部}

过 程 体

②过程首部以保留字 procedure 开头; ③过程名是由用户自定义的一个标识符,只用来标识一个过程; ④形参表缺省(当然要同时省去一对括号)时,称为无参过程; ⑤形参表的一般格式形式如下: [var] 变量名表:类型;?;[var] 变量名表:类型。 其中带 var 的称为变量形参,不带 var 的称为值形参。例如,下列形参表中: (x,y:real;n:integer;var w:real;var k:integer;b:real) 其中 x、y、n、b 为值形参,w、k 为变量形参。 调用过程时,通过值形参给过程提供原始数据,通过变量形参将值带回调用程序。因此,可 以说,值形参是过程的输入参数,变量形参是过程的输出参数。 ⑥过程体与程序、函数体基本相似,也包括说明和执行两个部分; ⑦过程的说明部分用来对过程体内所用的类型、常量、变量等进行说明,这些量只在本过程 内有效,退出过程体后,为这些量分配的存储单元被释放。这些量与过程体外的同名量无关; ⑧过程的执行部分以 begin 开头,以 end 结束(end 后有一个分号) 。与函数不同的是,不能 给过程名赋值,因为过程名不代表任何数据。 [例 5] 定义过程 fa 求 n!。 程序如下: procedure fa(n:integer); var k:integer; begin t:=1; for k:=2 to n do t:=t*k; end; 其中,变量 t 安排在主程序中说明,最终由 t 将 n!的值传回调用程序。 [例 6] 定义一个求三角形面积的过程 area。 程序如下: procedure area(a,b,c:real;var m:real); var p:real; begin p:=(a+b+c)/2; {程序首部} {局部变量说明}

m:=sqrt(p*(p-a)*(p-b)*(p-c)); end; 在过程 area 的形式参数表中, 定义了 a,b,c,m 四个参数, 其中 a,b,c 三个参数是值参, 参数 m 前有 var,则 m 为变量参数。该过程被调用后,由变量参数 m 将结果传回调用程序。 (二)过程调用 函数的调用方式是出现在表达式中,而过程调用是通过一条独立的过程调用语句来实现的。 一般形式为: <过程名>(<实在参数表>); 调用过程通过给出一个过程名,并用实在参数去代替形式参数。 说明: ①实参的个数、类型必须与形参一一对应; ②对应于值形参的实参可以是表达式,对应于变量形参的实参只能是变量; ③调用过程的步骤是:计算实参的值,传送给对应的形参,接着执行过程体,最后返回调用 处继续执行。 比较一下过程与函数的主要区别: ①过程一般会被设计成求若干个运算结果,完成一系列的数据处理,或与计算无关的各种操 作;而函数往往只为了求得一个函数值; ②过程无类型,不能给过程名赋值;函数有类型,最终要将函数值传给函数名; ③调用方式不同。函数的调用出现在表达式中,而过程调用是由独立的过程调用语句来完成 的; ④返回值的方法不同。函数值是通过函数名传回调用程序,过程则是通过实参将运算的结果 传给调用程序。 [例 7] 用过程编写程序求下图五边形的面积。 b1 b6 b5 b7 b4 程序如下: program ex5_7; var b1,b2,b3,b4,b5,b6,b7,s,sum:real; b3 b2

procedure area(a,b,c:real;var m:real); var p:real; begin p:=(a+b+c)/2; m:=sqrt(p*(p-a)*(p-b)*(p-c)); end; begin write(‘Please input b1,b2,b3,b4,b5,b6,b7:’); readln(b1,b2,b3,b4,b5,b6,b7); sum:=0; area(b1,b5,b6,s); sum:=sum+s; area(b2,b6,b7,s); sum:=sum+s; area(b3,b4,b7,s); sum:=sum+s; writeln(‘s=’,s:10:3); end. 程序中, 定义了一个能够从过程内部带回计算结果的过程 area, 每次的调用结果是保留在变 量 s 中的。读者可以比较该程序与前面例 4 用函数编写的程序的不同。 学习评价 1、编写一个求 k!的函数,调用此函数计算: d=

m! (0<n<m<8) n!(m - n)!
digit(349,5)=0

2、定义一个函数 digit(n,k),它回送整数 n 的从右边开始数第 k 个数字的值。例如: digit(12356,4)=5 3、定义一个过程实现将一个实数分解为整数部分和小数部分。 4、定义一个过程 swap,完成变量 a 和 b 的交换。

第六课 综合实践
实践目标: 1、巩固和检验本单元基础知识的掌握情况和综合应用能力; 2、学会如何将实际问题抽象为数学模型,并用 Pascal 编程解决。 实践案例: 1、约瑟夫问题。N 个人围成一圈,从第一个人开始顺时针报数,数到 M 的人出圈;再由下一 个人开始报数,数到 M 的人出圈;?。输出依次出圈的人的编号。N,M 由键盘输入。 2、对 6 到 60 之间的偶数验证哥德巴赫猜想:不小于 6 的偶数可分解成两个素数之和。 参考答案 实践案例 1 分析: 本题的难点在于下一次报数时,如何剔除掉上一次报数后已经出圈的人。由于对每个人只有 出圈或不出圈两种状态。 因此设置标志数组存放游戏过程中每个人的状态。 不妨用 1 表示出圈, 0 表示不出圈。首先,我们给标志数组赋初值为 0,表示每个都在圈上,在一次报数过后,如果某 个人已出圈,我们则将其标志数组元素的值置为 1,下一次报数过程中,当且仅当标志数组的元 素值为 0 时才累加计数。 最后模拟报数游戏的全过程。t 从 1 变化到 N 控制报数游戏的每节循环, 它实际上是一个指针变量,依次指向当前报数的位置;用 s 累计每节报数的数值;用 f 统计出的 总人数;因此游戏结束的条件就是 f=n。程序如下: program ex6_1; var n,m,s,f:integer; a:array[1..m] of 0..1; begin write(‘Input n,m=’); readln(n,m); for t:=1 to m do a[t]:=0; f:=0;t:=0;s:=0; writeln(‘Sequence coming out from queue is:’); repeat t:=t+1; if t=m+1 then t:=t+1; {圈中第 m+1 人等价于第一个人} {a 为标志数组}

if a[t]=0 then s:=s+1; if s=n then begin s:=0; write(t, ‘ ’); a[t]:=1; f:=f+1; end; until f=n; readln end. 实践案例 2 分析: 用布尔型函数 prime(x)判断 x 是否是素数,若是, 函数值为真,否则,函数值为假。算法如 下所示。 s1. t:=6 s2. while t≤60 do s. t1:=2; s4. repeat s5. t1:=t1+1; /* 找下一个素数 a */ s6. until prime(t1)and prime(t-t1); /*直到 a,b 都是素数*/ s7. writeln(i,’=’,t1,’+’,t-t1); s8. t:=t+2; s9. endwhile 程序如下: program ex6_2; var t,t1:integer; function prime(x:integer):boolean; var i:integer; begin if x=1 then prime:=false else if x=2 {出圈}

then prime:=true else begin prime:=true; i:=2; while (i<=round(sqrt(x)))and(x mod i<>0) do i:=i+1; if i<=round(sqrt(x)) then prime:=false; end; end; begin t:=6; while t<=60 do begin t1:=1; repeat t1:=t1+2; until prime(t1) and prime(t-t1); writeln(t,'=',t1,'+',t-t1); t:=t+2; end; end. {of prime}


相关文章:
freePascal教程.doc
freePascal教程 - PASCAL 语言程序设计 在上一册教材中, 我们
第1讲FreePascal 使用说明_图文.ppt
第1讲FreePascal 使用说明 - 全国青少年信息学奥林匹克竞赛入门级学习,Free Pascal 语言程序设计。第1讲FreePascal 使用说明,本节内容包括信息学奥林匹克竞赛介绍和...
Free Pascal从零开始编游戏(Display单元库使用说明)资....ppt
教程分为两个部分: 基础部分:第一到七章讲解Display单元库使用方法, 在阅读前请先阅读《一天学会Free Pascal教程; 高阶部分:第八到十三章讲解Display单元库...
pascal基础教程_图文.ppt
PASCAL基础教程 PASCAL基础教程信息学奥林匹克竞赛是一项益智性的竞 赛活动,核心...输出语句1、Free Pascal的输出语句有两种形式: 1)write(<输出项表>) 2)...
Win10使用pascal入门教程fpc.doc
Win10使用pascal入门教程fpc_计算机软件及应用_IT/计算机_专业资料。Win10下使用如何Free Pascal进行Pascal语言的编写,入门级教程,结合实例,快速掌握常见的程序编写例子...
Free_Pascal教程.doc
Free_Pascal教程 - 苏州市第一中学校 校本课程 -1- 目录 第一章
小学pascal语言教程ppt_图文.ppt
小学pascal语言教程ppt - 教师(PASCAL语言)培训讲 习 江东区教
64位Free pascal安装方法.pdf
64位Free pascal安装方法 - 1、如何在Windows64位系统下安装Free Pascal; 2、在Windows32位系统下和64位系统下都可以使用的Free Pascal; 3...
Lazarus安装和使用方法.doc
Lazarus安装和使用方法 - Lazarus 是基于 FreePascal 语言的、跨平台、免费、开源的开发工具,中国信息学奥赛选 用开发工具。Lazarus 系统安装方便,无须配置,使用与....
一天学会Free Pascal_图文.ppt
一天学会Free Pascal_计算机软件及应用_IT/计算机_专业资料 暂无评价|0人阅读|0次下载|举报文档一天学会Free Pascal_计算机软件及应用_IT/计算机_专业资料。本教程...
Pascal基本教程(2014学生版).doc
Pascal基本教程(2014学生版) - Pascal 基本教程 第1章 第2
信息学奥赛PASCAL教程.pdf
信息学奥赛PASCAL教程_电子/电路_工程科技_专业资料。___...语句指定程序名称,例如,这里告诉编译器,程序名称为 ex01,在 FreePascal 中可以省略。 行 2:begin.....
从Pascal 到 C++的教程.pdf
从Pascal 到 C++的教程_高等教育_教育专区。从 Pascal 到 C++ 作者 / Menci ...有?一个神奇的集成开发环境叫 FreePascal IDE ! 听说过 ACM 没? 头?文件 !...
信息学奥林匹克竞赛教程.pdf
信息学奥林匹克竞赛教程 - 第一课 初识 Pascal 语言 信息学奥林匹克竞赛
pascal教程-自学完整版_图文.ppt
pascal教程-自学完整版 - Pascal教程 目录 ? ? ? ? ? ?
Free Pascal 教程.doc
Free Pascal 教程_生活休闲。初级Free Pascal 教程 Free Pascal 教程目录第一章 简单 ... 3 第一节 Pascal 程序结构和基本语句 ......
pascal教程 自学完整版_图文.ppt
pascal教程 自学完整版 - Pascal教程 目录 ? ? ? ? ? ?
12多重循环结构free pascal教程_图文.ppt
12多重循环结构free pascal教程 - 多重循环结构 例题1 ? ? 求
01第一课 Pascal编译器的使用_图文.ppt
01第一课 Pascal编译器的使用 - pascal语言教程,适合noip学习之用。共有14个课件,详细介绍pascal语法,针对性强,可自学也可用于教学,赶快下载吧。
信息学奥赛培训教程(PASSCAL第一课)_图文.ppt
信息学奥赛培训教程(第一课)学习编程的好处 关于“信息学奥赛培训”问答 什么是...安全退出Free Pascal : ALT+X 开学第一课 7、Turbo Pascal编辑环境其他常用...
更多相关标签: