p26 记录
p26记录
一、记录的概念
在程序中对于组织和处理成批的数据来说,数组是一种十分方便的数据类型,而且数组的使用很灵活。但是数组也有一个显而易见的缺陷,那就是:一个灵敏组中的所有数据元素都必须具有相同的类型。但有一批数据是由性质各不相同的多种成分组成的。例如,要处理100名学生的档案情况,每个学生的数据包含以下几个数据项:
学号 字符串类型
姓名 字符串类型
年龄 整型
性别 字符型
是否团员 布尔型
家庭住址 字符串类型
电话号码 字符串类型
语文成绩 实型
数学成绩 实型
英语成绩 实型
政治成绩 实型
从上面我们可以看出,这种数据的各个成分是完全不同的数据类型,我们很难用数组或其他数据结构来形象有效地组织和处理。为此,Pascal为我们提供了一种叫“记录”的数据类型。
记录是由一些称为“域(又叫成员)”的数据成分所组成的一种构造类型。其中每个域(成员)可以具有不同的类型。,例如,对上面的例子我们就可以把它定义成一个记录类型,它是由11个“域(成员)”所组成:学号、姓名、年龄、性别、是否团员、家庭住址、电话号码、语文成绩、数学成绩、英语成绩、政治成绩。其中学号、姓名、家庭住址、电话号码是字符串类型;年龄是整型;性别是字符型;是否团员是布尔型;其他域为实型。
正因为在一个记录中允许多种不同类型的数据共存,所以记录类型在程序设计中用途非常广泛。
二、记录类型的定义及记录变量的说明
记录类型由用户根据需要在程序的type区中自行定义,基本形式为:
type 类型标识符=record
域名1:类型;
域名2:类型;
…
域名n:类型;
end;
如,以上有关学生的数据可以定义成以下的记录:
type studata=record
num:string[6];
name:string[10];
age:integer;
sex:char;
ty:boolean;
add:string[30];
tel:string[11];
chinese:real;
maths:real;
english:real;
politics:real;
end;
对于记录类型的定义,有以下几点需要注意:
(1)在一个记录类型中,不能有相同的域名;在多个不同的记录类型中可以有相同的域名;
(2)在记录中,各个域的类型可以是简单的数据类型,也可以是数组等其他构造类型。
如:在定义学生记录前先定义一个数组类型用来存放成绩:
type score:array[1..4] of real;
则,在上面的记录类型定义中,可以将几门课的成绩域(即:chinese,maths,english,politics)合并成一个域:s:score;
也可以直接写在一起,即s:array[1..4] of real;
这样,s[1]、s[2]、s[3]、s[4]就分别表示一位学生的语文、数学、英语、政治成绩。
定义了记录类型后,我们就可以定义一些记录型变量了,记录型变量的定义同其他类型变量的定义方式相同。如:var stu:studata;这样,就定义了一个记录类型变量stu,用来存放一位学生 的数据。
可以将记录类型的定义和记录型变量的定义放在一起,如定义一个学生的出生日期:
type date=record
year:1980..1999;
month:1..12;
day:1..31;
end;
var d:date;
可以合并如下 :
var d:record
year:1980..1999;
month:1..12;
day:1..31;
end;
三、记录成员的引用
在使用记录时,需要注意以下两点:
(1)对记录的操作,除了可以进行整体赋值外,只能对记录的成员逐个引用;
(2)记录的每个成员只能参加其基类型所允许的运算操作。
假设有以下说明:var a,b:studata;
即a,b属于同记录类型,若记录a中每个成员都已有确定的值,那么在程序的执行部分就可以执行如下语句:b:=a;
它表示将a记录的每个成员的值赋给b记录的各个同名成员。
对记录成员的引用方式有两种,一种为直接引用,一种为开域引用。
直接引用方式如下:记录名.域名
如上面定义学生出生日期记录d中,就有3个变量,分别表示为d.year、d.month、d.day。对域变量的赋值可以通过read语句,也可以通过赋值语句。如:
read(d.year,d.month,d.day);
d.year:=1980;d.month:=10;d.day:25;
对于域类型又是数组的,引用形式具体为:
记录名.数组名[i];
如读入一位学生的4门成绩,可以写成:
for i:=1 to 4 do
read(studata.s[i]);
在程序中对记录进行处理时,经常需要引用同一记录中的不同域,如果每次都要用“记录名.域名”的形式,非常麻烦。为此,Pascal专门提供了一条with 语句以简化对记录的引用,称为“开域引用”,with语句又叫“开域语句”。
with 语句的格式为:
with 记录名 do 语句;
如下面的程序段:
write(‘input year:’);
readln(d.year);
write(‘input month:’);
readln(d.month);
write(‘input day:’);
readln(d.day);
可以写成:
with d do
begin
write(‘input year:’);
readln(year);
write(‘input month:’);
readln(month);
write(‘input day’);
readln(day);
end;
关于开域语句有以下几点说明:
(1)在with ...do 后面语句中使用记录的域时,只要简单地写出域名就可以了,域名前的记录变量和“.”均可省略;
(2)with ...do 后面的语句可以是一个简单语句,也可以是一个复合语句;
(3)由于开域语句是一种构造语句,因此Pascal语言规定在with ...do后面的语句当中不能改变已开域的记录;
即假设有以下开域语句;with x do 语句y;
在执行时,不允许语句y影响已开域的记录x 。所以,下面的语句是不合法的:
with a[i] do
begin
...
i:=i+1;
end;
这是因为i:=i+1改变了a[i],所以造成不能正常运行。
(4)关于记录的嵌套:在定义一个记录类型中也可以引用另外一个记录类型,但记录的嵌套层次是有限的。
如:下列记录的说明是合法的:
var x:record
i:integer;
y:record
j:0..5;
k:real;
end;
m:real;
end;
此时,若使用开域语句输入数据则可以写成:
with x do
begin
read(i);
with y do
read(j,k);
read(m);
end;
也可以简化成:
with x,y do
read(i,j,k,m);
四、记录数组
上面,我们用var stu:studata;定义了一个记录型变量stu用来存放一位学生的数据,如果有100位学生的数据该如何组织和处理呢?这就要用到“记录数组”。
所谓记录数组是指一个数组的每个元素类型(基类型)又是一个记录类型。如:
var students:array[1..100] of studata;
这样,就定义了一个记录数组students ,这个数组的每个元素students[i]又都是一个记录类型。
记录数组的引用形式为:
数组名[i].域名
如:第十位学生的年龄可以表为:
students[10].age
注意:
(1)要理解“记录数组”和“域的基类型是数组类型的记录”这两个概念,、区分两者的使用场合和具体引用形式。后者的引用形式为:记录名.数组名[i];
(2)Pascal允许把记录定义成压缩式的,只需在关键字record之前加上packed,其意义类似于压缩数组。
五、变体记录
在我们前面所讲的记录中,每个记录的域个数和类型是固定不变的。然而实际问题中,仅有这样的数据类型是不够的。如:在学生档案的登记中,对男同学要求只登记身高,而对女同学则要求只登记体重,那么这样的数据想要定义成记录该怎么办呢?这就用到“变体记录”。
如上例就可以定义成这样的记录:
type stu=record
score:array[1..6] of 0..100;
age:integer;
case sex:char of
‘m’:(weight:70..150);
‘f’:(high:real);
end;
这里所定义的stu是一个变体记录类型,记录中score,age 域分别表示学生的各科成绩和年龄,是男女同学共同的。而从case 之后则根据性别登记不同的内容,若sex是‘m’则登记女生的体重,若sex是‘f’则登记男生的身高,两者是可选的,并且只能选其一。实际上case后面就是记录的“变体”部分,of后面列举了sex的所有可能情况,‘m’和‘f’就是情况常量,每种情况常量之后跟着一个冒号和一对圆括号,括号内列出了每种情况的域名和基类型。情况常量后面可以不写域名,但必须写冒号和一对圆括号。
例1输入20位学生的数据记录(包含学号、姓名、性别、年龄、成绩五个域),按成绩从高到低排序输出。
程序代码如下:
program aa;
const n=20;
type student=record
no:integer;
name:string[15];
sex:char;
age:10..18;
score:real
end;
var stu:array[1..n] of student;
t:student;
i,j:integer;
begin
writeln(‘input’,n,’no name sex age score’);
for i:=1 to n do {对齐,用开域语句读入n个学生的记录}
with stu[i] do read(no,name,sex,age,score);
for i:=1 to n-1 do {排序}
for j:=i+1 to n do
if stu[i].score<stu[j].score
then begin
t:=stu[i];
stu[i]:=stu[j];
stu[j]:=t;
end;
writeln(‘no name sex age score’);
for i:=1 to n do {对齐,用开域语句输出排序好的N个学生的记录}
begin
with stu[i] do
write(no:8,name:8,sex:2,age:4,score:8:1);
writeln;
end;
end.
例2编程读入一组日期,输出第二天的日期。输入日期的格式是月、日、年,输出格式是月/日/年。如:输入7 31 1994 ,输出8/1/1994
问题分析:可以用一个记录变量today来表示日期,遇到一个日期后,应判断输入的日期是否为当月的最后一天或当年的最后一天,做相应的处理便可。
程序代码如下:
program aa;
const n=10;
type date=record
month:1..12;
day:1..31;
year:1900..2100;
end;
var today:array[1..n] of date;
i:integer;f:boolean;
maxday:28..31;
begin
f:=true;
for i:=1 to n do
with today[i] do readln(month,day,year);
for i:=1 to n do
with today[i] do
begin
case month of
1,3,5,7,8,10,12:maxday:=31;
4,6,9,11:maxday:=30;
2:if ((year mod 4=0) and (year mod 100<>0))
or (year mod 400=0)
then maxday:=29
else maxday:=28;
else f:=false;
end;
if day=maxday
then begin
day:=1;
if month=12 then begin
month:=1;year:=year+1;
end;
else month:=month+1;
end;
else if (day>maxday) or (day<0)
then f:=false
else day:=day+1;
if f then writeln(month:2,’/’,day:2,’/’,year:4);
else writeln(‘data error!’)
end;
end.

浙公网安备 33010602011771号