admin 管理员组

文章数量: 1086019


2024年12月31日发(作者:agreed是什么意思)

第四单元 PASCAL语言程序设计

在上一册教材中,我们已经初步了解了PASCAL语言的三种基本结构、程序设计的基本思想

和方法。本单元是在上册内容的基础上进一步深入学习PASCAL语言的数组、子程序、字符串处

理等基础知识。随着学习的深入,程序设计的题目类型越来越广泛,难度也越来越大,程序设计

者不能“完全”把求解的任务推给计算机,而应对题目作较充分的分析,用较优的算法去求解,

因此在计算机上编程解题是一项极好的实践活动,它可以训练观察能力、逻辑思维能力、形式化

描述问题能力、规划能力、动手动脑分析问题和解决问题的能力。

第一课 循环结构的程序设计

在程序设计中,经常处理一些需要重复执行某些操作的问题,这就需要循环结构程序设计,

循环结构是程序设计的三种基本结构之一,循环结构是指当某个条件满足时反复执行某些语句,

否则不执行。利用循环结构,使得我们能用少而精的程序编码,来完成大量的重复计算。

在Pascal语言中,实现循环程序设计的主要语句有For语句(计数循环)、While语句(当型

循环)、Repeat语句(直到型循环)。上册教材已经介绍了For语句,本节课介绍While语句、Repeat

语句及多重循环结构。

一、 While语句结构

For循环语句适用于已知次数的循环结构,而在实际的问题中,有时我们并不能确切知道循

环要执行多少次,我们判断是否结束循环的依据是某个条件是否满足,比如“当„„时就做„„”,

或者是“一直做到„„时为止”,这种循环我们称之为条件循环。在Pascal中条件循环语句有两

种,分别是While循环语句和Repeat循环语句。下面分别介绍这两种语句的使用方法。

While语句用于“当满足某一条件时进行循环”的情况,因此它也被称为“当型循环”。While

语句的语法格式如下:

While <布尔表达式> do <循环体语句>;

While循环语句的执行流程如图1-1-1所示。

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

布尔表达式

False

True

循环体

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所示。

Repeat语句使用说明:

1、先执行循环体,然后判断布尔表达式的值,为false,则继续循环,为true则终止循环;

2、为了使repeat循环重复能终止,与while循环一样,循环体中一定要有影响布尔表达式值

的操作,否则该循环就是一个死循环;

False

布尔表达式

True

循环体

图1-2-1 Repeat循环语句执行流程

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);

333

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 {枚举n个数}

begin

prime:=true;k:=2;

while (k

begin

if i mod k=0 then prime:=false;

inc(k); {相当于k:=k+1}

end;

if prime then

begin

write(i:5);

s:=s+1;

if s mod 10=0 then writeln; {满10个就换行}

end;

end;

end.

注意:上述程序k

[例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的最大值:2

2

+4

2

+6

2

+„+n

2

<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、d2为枚举类型daytype,它们的取值只限于类型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。

同时,枚举值是可以由大小来区别的,关系式sunfri的值是false。

用序号函数可以求出枚举值的序号:

ord(sun)=0 ord(mon)=1 ord(fri)=5

用succ和pred函数可以求出枚举值的后继和前趋:

succ(sun)=mon succ(wed)=thu pred(wed)=tue pred(sat)=fri

当然,枚举类型中的第一个元素没有前趋,最后一个元素没有后继。

可见,枚举值可以作为序号函数(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; {case m}

end; {for p}

writeln;

end; {if}

writeln(‘total:’,s);

end.

运行后共输出60种不同的信号方案。

由于枚举类型是一种顺序类型,故枚举类型的变量可以作为循环变量。

二、子界类型

(一)子界类型的定义

在实际应用中,有些数据的变化范围只局限在某一数据类型的某一确定的区域,Pascal语言

中,对只取某一已经定义了的顺序类型的值的某一范围的这类问题,定义了子界这一数据类型。

例如,一年中的月份不超过12,一月中的天数不超过31。采用子界类型便于检查数据的合法性,

增加程序的可读性,使解决这类问题既符合常规概念,又自然清晰,更好保证了程序运行的正确

性。

子界类型定义的一般格式为:

type

<子界类型标识符>=<常量1>..<常量2>

其中常量1称作下界,常量2称作上界,且上界必须大于下界。下界和上界可以是整型、字

符型、布尔型、枚举型等顺序型的两个常量,但必须是同一种顺序类型,该顺序类型称为子界类

型的“基类型”。一个子界类型的取值范围是其基类型的取值范围中从下界到上界的连续一段,子

界类型的值域实际上就是基类型值域的一个子集。

下面的类型定义部分中定义了一个枚举类型和四个子界类型:

Const

n=150;

Type

week=(sun, mon, tue, wed, thu, fri, sat);

;

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、m和d的值是否越界,在程序中加了编译器指示{$R+}。

若在运行该程序时输入越界的年份或月份,例如输入年份为2001,则中断程序的运行,并出现如

下出错信息:

range check error

学习评价

1、设有四种水果:苹果、橘子、香蕉和菠萝,现要任取其中3种水果,不能重复,不计先

后顺序,请编写程序列出所有可能的取法。

2、输入年、月、日,输出该月有几天。

3、类型定义

type ren=’A’..’F’;

用A至F表示6个人,输出6个人相互握手的各种情况,统计握手的次数。

第三课 数组

在前面我们所学的变量都是独立的变量,即变量之间没有内在的联系,一个变量只能表示一

个数据。如果处理的数据个数较少,仅使用简单变量编程就够了。如果处理大批数据,而且这些

数据之间有某种内在联系,例如,一个工厂100名工人,要给这100人记入每个月的工资,并求

月平均工资,那么用简单变量处理就很不方便了。像此处所说的求平均工资的问题,需要设100

个变量:S

1

,S

,┉,S

100,

要写出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

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; {假设有10个数据需排序}

var a:] of integer;

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.

[想一想] 如果需要将一组数据从大到小排序,程序该如何调整?

[例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:axn] 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

if x>a[n] then k:=n;

for i:=1 to n do

if(x>a[i])and(x

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

1 2 1 1 2 1

1 3 3 1 1 3 3 1

1 4 6 4 1 1 4 6 4 1

1 5 10 10 5 1 1 5 10 10 5 1

图3-2-2 图3-2-3

分析:观察图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; {需要打印前10行}

i,j:integer;

begin

yh[1,1]:=1; {设定第一行的值}

for i:=2 to 10 do {设定第2~10行的值}

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);

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’; {结果为true}

‘ASC’<‘ASCII’; {结果为true}

‘3’<‘24’; {结果为false}

字符串的赋值操作是指将一个字符串表达式的结果赋给一个字符串变量,它和字符串的连接

运算一样,受到字符串变量的长度限制,一旦超出定义好的长度则后面的字符将被删除掉。

三、字符串标准过程和标准函数

在Free Pascal中提供了如下与字符串类型有关的标准过程和函数,以便于字符串数据的处

理应用。

函数名 意义 结果类型

string

示例

s1:= ‘123’;s2:= ‘456’;

concat(s1,s2,„,sn) 连接字符串

返回字符串s中第

copy(s,index,count) index位置开始的长度

为count的子串

length(s) 返回字符串s的长度 integer

string

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);

结果:s=‘Turbo Pascal’

i:=1234;

str(i,s);

结果:s=‘1234’

insert(s1,s,index)

str(x:m:n,s)

把字符串s转换成相应的数值k,codes:= ‘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; {定义求n!的函数fac}

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

b2

b6

b5 b7

b3

b4

图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

b2

b6

b5 b7

b3

程序如下:

program ex5_7;

var

b1,b2,b3,b4,b5,b6,b7,s,sum:real;

b4

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-n)!

2、定义一个函数digit(n,k),它回送整数n的从右边开始数第k个数字的值。例如:

digit(12356,4)=5 digit(349,5)=0

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:] of 0..1; {a为标志数组}

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人等价于第一个人}

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; {of prime}

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.


本文标签: 类型 数组 循环 变量