该内容为学习 《PASCAL程序设计 第2版》郑启华编著 的笔记,部分与c语言对比学习,方便记忆。
集合类型
定义
TYPE
<集合类型标识符> = set of <基类型>;
VAR
<集合类型变量表>: <集合类型标识符>;
基类型必须是有序类型(整型子界、字符型、布尔型、枚举型等),例:
TYPE
digit = set of 1..9;
VAR
od, even: digit;
BEGIN
od := [1, 3, 5, 7, 9];
even := [2, 4, 6, 8];
运算
在处理集合前,一般需要赋予初值(常为空集或全集):
od := []; {空集}
od := [1..9]; {全集}
1. 并交差(要求2集合类型相容)
{并,+}
[1, 3, 4] + [1, 2, 4] --> [1, 2, 3, 4]
['a', 'c', 'f'] + ['b', 'c', 'd', 'f'] --> ['a', 'b', 'c', 'd', 'f']
{交,*}
[1, 3, 4] * [1, 2, 4] --> [4]
['a', 'b'] * ['c', 'd'] --> []
{差,-}
[1, 3, 4] - [1, 2, 4] --> [3]
['a', 'b', 'c'] - ['a', 'c', 'b'] --> []
集合中顺序不重要。
2. 关系运算(=、<>、<=、>=)
关系运算的结果为布尔型:
{=和<>用来检查2个集合是否包含同样的元素}
[1, 3] = [3, 1] --> true
[1, 3] <> [1, 3] --> false
{<=用来决定子集关系}
[1, 3] <= [1, 2, 3, 4] --> true
[] <= [1, 3] --> true
{>=用来决定包集关系}
[1, 3] >= [1, 3] --> true
[2, 4] >= [1, 2] --> false
另有新运算 IN ,用于决定一个特定的元素是否在集合中:
1 in [1, 2, 3] --> true
1 in [3,2 4] --> false
以下2种写法等价:
(ch>='0') and (ch<='9')
ch in ['0'..'9']
3. 输入和输出
集合变量不能直接输入输出,输入通常为:将集合置为空集,然后逐个读入集合的每个元素值,将构成的单指集合依次+进原集合变量;输出通常为:用IN依次检查该集合基类型中的每一个元素是否在该集合中,若在则依次输出对应元素。
例:输入一串字符,以'?'结束,组成元音字母集合s1,辅音字母集合s2,然后输出两集合元素以及元素个数。
PROGRAM sets (input, output);
VAR
s1, s2: set of 'a'..'z';
n1, n2: integer;
ch: char;
BEGIN
s1 := [];
s2 := [];
n1 := 0;
n2 := 0;
read(ch);
write(ch);
while ch<>'?' do
{判断输入的ch是元音还是辅音,加入对应的集合}
BEGIN
if ch in ['a'..'z']
then if ch in ['a', 'e', 'i', 'o', 'u']
then s1 := s1+[ch]
else s2 := s2+[ch];
read(ch);
write(ch);
END;
writeln;
{输出}
for ch:='a'to'z' do
if ch in s1
then BEGIN
write(ch);
n1 := n1+1;
END;
writeln;
writeln('n1=', n1);
for ch:='a'to'z' do
if ch in s2
then BEGIN
write(ch);
n2 := n2+1;
END;
writeln;
writeln('n2=', n2);
END.
可能的输入输出:
sdgdgdffdsvsdgda?
sdgdgdffdsvsdgda?
a
n1=1
dfgsv
n2=5
类型间的关系
关系:同一、 相容、赋值相容。
符合如下任一条则说明两个变量属于该关系:
同一(对称) | 相容(对称) | 赋值相容(不对称,e对v赋值相容,v:=e) |
1. 使用同样的类型标识符说明 2. 类型标识符不同,但已被形如t1=t2的说明定义为等价 3. 在同一变量说明语句中使用 |
1. 它们是同一的非结构变量 2. 它们是同一类型的子界,或一个是另一个的子界 3. 它们是有相容的基类型的集合类型 4. 它们是具有同一长度的串类型 5. 整型和实型之间 |
1. e和v是类型同一的非文件类型 2. v是实型,e是整型或整型的子界 3. e和v是相容的简单类型,且e的值是v所允许的值 4. e和v是相容的集合类型,且e的每一个值都是v所允许的值 5. e和v是相容的串类型
|
三者之间不存在蕴含关系,关系图如下:
记录类型
记录是2个或多个有关数据项的汇集,一个记录的每个分量可以具有不同的类型,类似于c语言中的结构体。
定义
TYPE
<记录类型标识符> = RECORD
<域标识符表>: <类型>;
...
<域标识符表>: <类型>
END;
域标识符表可以是一个标识符或多个标识符,在多个标识符的情况下,每个标识符之间用 ','(逗号) 隔开。
年月日的记录如下:
TYPE
mont = (jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec);
date = RECORD
month: mont;
day: 1..31;
year: integer
END;
VAR
day1, day2: date;
WITH语句(开域语句)
一般形式:
with <记录变量名表> do
<语句>
语句可以是单条语句,或是用BEGIN..END括起来的多条语句。
对记录的赋值:
{原始的赋值方法}
day1.month := mar;
day1.day := 3;
day1.year := 2020;
day2 := day1;
{使用with语句后}
with day1 do
BEGIN
month := mar;
day := 3;
year := 2020;
END;
记录数组
类似于c语言的结构体数组,一个数组的分量是记录类型称为记录数组(记录数组首先是个数组,数组的分量是记录)。
例:输入全班学生的姓名和成绩,按照某一标准给学生成绩评定等级,最后输出各位学生的姓名、成绩、等级。
思路:定义一个记录类型student,它的域包括姓名name,成绩score,等级grade。其中name是一个字符串(由紧缩型数组定义),score是实型,grade是字符型。然后再定义一个记录数组类型studenta,它的分量类型为student,存储班上所有学生的信息。
CONST
n = 30; {班级学生人数}
TYPE
alfa = packed array[1..15]of char {姓名的字符串}
student = RECORD
name: alfa;
score: real;
grade: char;
END;
studenta = array[1..n]of student;
VAR
students: studenta;
给记录数组赋值时:
with students[i] do
BEGIN
name := 'li hong '; {共15位}
score := 93.5;
grade := 'A';
END;
层次记录
记录的域为记录类型,则为记录的嵌套,也就是层次记录。类比多维数组,记录的嵌套也可以是多重的。
例:职员的记录类型,包括编号,姓名,生日,家庭地址。
TYPE
alfa = packed array[1.15]of char;
date = RECORD
year: integer;
month: 1..12;
day: 1..31;
END;
address = RECORD
city: alfa;
street: alfa;
streetnum: integer;
END;
employee = RECORD
num: integer;
name: alfa;
birth: date;
home: address;
END;
VAR
programmer: employee;
其中生日和家庭地址的类型是记录,给该层次记录赋值可写为:
programmer.num := 1;
programmer.birth.year := 1998;
programmer.home.city := 'changsha ';
可使用with语句简写:
with programmer, birth, home do
BEGIN
num := 1;
year := 1998;
city := 'changsha ';
END;
需注意在with..do之间的顺序问题,首先programmer必须在第一位。如果birth和home不是并列关系,那么顺序需层次渐进。
在上例中birth和home是并列的,如果这2条记录中没有同名的域名,那么顺序无所谓;如果有同名域名,为了不引发问题,需分开写:
with programmer, birth do
BEGIN
num := 1;
year := 1998;
with home do
BEGIN
city := 'changsha ';
END;
END;
记录变体
1. 一般形式:
TYPE
<记录类型标识符> = RECORD
{固定部分}
<域标识符表>: <类型>;
...
{变体部分}
case <标志域>: <类型> of
<常量表>: (<域表>);
...
END;
- 固定部分和变体部分出现的域名不能相同
- 如果常量表中没有对应的域,此时域表为空,用一个()空括号对表示
- <域表>内也可以有变体部分,变体部分必须在对应的固定部分之后
- case..of 可以不需要end作为结束
2. 例:接上个例子,职员的记录类型为编号、姓名、生日、婚姻状况。
其中,婚姻状况为,已婚、离异、单身。对于已婚的职员,需附加配偶姓名和孩子个数;对于离异的职员,需附加离婚日期;对於单身的职员,需附加是否单独住。
TYPE
alfa = packed array[1..15]of char;
date = RECORD
year: integer;
month: 1..12;
day: 1..31;
END;
marristat = (married, divorced, single);
employee = RECORD
{固定部分}
num: integer;
name: alfa;
birth: date;
{变体部分}
case ms:marristat of
married: (spousename: alfa;
child: integer);
divorced: (divorcedate: date);
single: (livesalone: boolean);
END;
其中,marristat为枚举类型,ms是类型为marristat的特殊域,称为标志域。当ms取值为married时,会附加定义2个域spousename和child,另2个分支不会被定义,因此此时programmer.spousename和programmer.child可以被引用,而programmer.livealone不存在。
在定义变体部分时,下面2句等价:
case ms:marristat of
ms: marristat;
case marristat of
3. 读记录
VAR
marry, live: char;
BEGIN
{读固定部分}
with employee, birth do
readln(num, name, year, month, day);
{读变体部分}
with employee do
BEGIN
{读标志域值}
read(marry);
if marry='m'
then ms:=married
else if marry='d'
then ms:=divorced
else ms:=single;
{根据标志域值读附加域值}
CASE ms OF
married: readln(child, spousename);
divored: with divorcedate do
readln(year, month, day);
single: BEGIN
readln(live);
livesalone := (live='t');
END;
END;{end case}
END;{end with}
END.
首先键盘输入职员编号、姓名、生日的年月日,然后输入一个字母m,d,s代表该职员的婚姻状况。如果输入的是m,则接着输入孩子个数和配偶姓名;如果输入的是d,则接着输入离婚的年月日;如果输入的是s,则接着输入t或者f,t代表单独住f代表不是。