该内容为学习 《PASCAL程序设计 第2版》郑启华编著 的笔记,部分与c语言对比学习,方便记忆。
函数与过程程序设计
函数
1. 定义:
FUNCTION <函数名>(<参数表>):<函数类型>;
<说明部分>;
BEGIN
<函数体>
END;
- 函数体由给函数名赋值的语句构成,函数类型即函数的返回值类型,返回的是函数名的最终结果。
- 如果没有参数,则括号也不需要。
- 如果有多种类型的参数,相同参数之间用逗号,不同参数之间用分号。
- 如果函数中需要用到变量,需在说明部分定义,为局部变量。
2. 调用:
<函数名>(<实在参数表>)
如果没有实在参数,则括号也略去。
3. 例子:
定义函数rand产生随机数,使得生成一些两位整数x两位整数的式子,作答后判断正误。
PROGRAM game(input, ouput);
VAR
x, y, z, answer: integer;
seed: real;
{产生一个[0,1)的随机数}
FUNCTION rand(VAR seed:real):real;
CONST
a= 93.0;
m = 8192.0;
c = 1.0;
BEGIN
seed := a*seed+c;
{seed/m - trunc(seed/m)获取了seed/m的小数部分,其再乘以m,获得一个[0,m)的数}
seed := round( (seed/m - trunc(seed/m)) * m );
{seed的范围为[0,m),计算得出的rand为[0,1)}
rand := seed/m;
END;
{主程序}
BEGIN
seed := 0.7823;
REPEAT
{产生2个两位的随机整数x, y}
x := trunc(rand(seed)*100);
y := trunc(rand(seed)*100);
writeln(x:2, '*', y:2, '=?');
read(answer); {输入算式答案}
{判断正确与否}
z := x*y;
if answer=z
then writeln('yes!')
else writeln('no! it is ', z)
UNTIL answer=-1
END.
- 函数定义中,参数表中的VAR表明形式参数seed为变量参数,若没有VAR则为值参数。
- 变量参数的改变会影响是实在参数的值,值参数的改变不影响实在参数的值。
上述例子中,rand函数为产生随机数的某种方法,可得到一个[0,1)之间的数。为了使每次获得的随机数不同,需定义函数中的参数seed为变量参数,随着函数的调用而改变参数的值。
输出结果:
0*84=?
0
yes!
13*98=?
4567
no! it is 1274
83*26=?
543
no! it is 2158
69*34=?
435
no! it is 2346
20*27=?
345
no! it is 540
过程
1. 定义:
PROCEDURE <过程名>(<形式参数表>);
<说明部分>;
BEGIN
<过程体>
END;
- 参数表中包括值参数和变量参数,过程的结果由变量参数送回,可以包括多个结果或无结果。
- 过程体的通常任务是对值参数进行计算,计算结果赋值给变量参数。
- 无参数时,形式参数表和括号都应略去。
2. 调用:
<过程名>(<实在参数表>)
过程的调用必须作为一个单独的语句。
3. 例子:
打印表头。
PROGRAM table(input, ouput);
{打印表头}
PROCEDURE printhead;
VAR
i: integer;
BEGIN
for i:=1 to 21 do
write('*');
writeln;
writeln('* this is the table *');
for i:=1 to 21 do
write('*');
writeln;
END;
{主程序}
BEGIN
printhead;
END.
该过程既无结果也无参数,输出:
*********************
* this is the table *
*********************
对比
- 函数通过函数名回送一个结果值,需说明返回值的类型;过程的结果由参数送回,可以包括多个结果或无结果。
- 函数体中必须要有给函数名赋值的语句,过程体不需要也不能有。
- 函数的调用可以出现在表达式中,过程的调用必须作为一个单独的语句。
例:输入x值,计算y。有 , 。
函数实现:
PROGRAM ysh1(input, ouput);
VAR
x, y: real;
{函数}
FUNCTION sh(t:real):real;
BEGIN
sh := (exp(t)-exp(-t))/2;
END;
{主程序}
BEGIN
read(x);
y := sh(1+sh(x))/(sh(2*x)+sh(3*x));
writeln('x=', x, ', y=', y);
END.
过程实现:
PROGRAM ysh2(input, ouput);
VAR
x, y, s1, s2, s3: real;
{过程}
PROCEDURE sh(t:real; VAR s:real);
BEGIN
s := (exp(t)-exp(-t))/2;
END;
{主程序}
BEGIN
read(x);
sh(x, s1);
sh(1+s1, s1);
sh(2*x, s2);
sh(3*x, s3);
y := s1/(s2+s3);
writeln('x=', x, ', y=', y);
END.
输入输出结果:
5
x= 5.0000000000000000E+000, y= 1.3899708997173883E+026
函数与过程作为参数
作为参数的函数或过程本身只能有值参数。使用:
PROGRAM test(input, output);
VAR
s1, s2: real;
FUNCTION f1(p1:real):real;
BEGIN
...
END;
FUNCTION f2(function f(p:real):real; a:real):real;
BEGIN
...
END;
BEGIN
read(s1);
s2 := f2(f1, s1);
END.
嵌套与递归
1. 嵌套:
形式1:定义2个并列的函数,主程序可以调用2个函数,函数2能调用函数1,函数1不能调用函数2。
PROGRAM test(input, ouput);
VAR
x1, x2, y1, y2: real;
FUNCTION fun1(p1:real):real;
BEGIN
...
END;
FUNCTION fun2(p2:real):real;
BEGIN
...
fun2 := fun1(p2);
END;
BEGIN
read(x1, x2);
y1 := fun1(x1);
y2 := fun2(x2);
writeln(y1, y2);
END.
形式2:在函数1中定义函数2,则函数1能调用函数2,函数2不能调用函数1,主程序只能调用函数1。
PROGRAM test(input, ouput);
VAR
x1, y1: real;
FUNCTION fun1(p1:real):real;
FUNCTION fun2(p2:real):real;
BEGIN
...
END;
BEGIN
fun1 := fun2(p1);
END;
BEGIN
read(x1);
y1 := fun1(x1);
writeln(y1);
END.
形式3:第一种情况中加上向前引用说明和保留字FORWARD,则函数1和2能互相调用。
PROGRAM test(input, ouput);
VAR
x1, x2, y1, y2: real;
FUNCTION fun2(p2:real):real;
forward;
FUNCTION fun1(p1:real):real;
BEGIN
...
fun1 := fun2(p1);
END;
FUNCTION fun2;
BEGIN
...
fun2 := fun1(p2);
END;
BEGIN
read(x1, x2);
y1 := fun1(x1);
y2 := fun2(x2);
writeln(y1, y2);
END.
类似于c的声明,不过一般很少有函数要互相调来调去,按顺序写就好了,或者干脆所有函数都声明。
2. 递归:
- 直接递归:函数1直接调用其本身。
- 间接递归:函数1调用函数2,函数2又调用函数1。
例:递归计算 n! 。
PROGRAM facn(input, ouput);
VAR
x, y: integer;
FUNCTION fac(n:integer):integer;
BEGIN
if n=0
then fac := 1
else fac := n*fac(n-1)
END;
{主程序}
BEGIN
read(x);
y := fac(x);
writeln(x, '!=', y);
END.
输出结果:
3
3!=6
枚举与子界
枚举
1. 定义
TYPE
<枚举类型标识符> = (<标识符>, ..., <标识符>);
VAR
<枚举类型变量表>: <枚举类型标识符>;
枚举值只能是标识符(以字母开头的字母数字组合),每个枚举值只能出现在一个枚举类型定义中,并且只能在该定义中出现一次。
例:
TYPE
day = (sunday, monday, tuesday);
month = (jan, feb, mar);
VAR
payday, firstday: day;
yearend, curmonth: month;
2. 运算
可以将枚举值直接赋值给枚举变量,或者同类型的枚举变量赋值给另一个枚举变量,例:
{枚举值赋给枚举变量}
curmonth := mar;
firstday := monday;
{枚举变量赋给同类型枚举变量}
payday := firstday;
枚举变量可进行ord,pred,succ和关系运算,例:
{序号从0开始}
ord(jan) = 0
{第一个枚举值无前导,最后一个枚举值无后继}
pred(monday) = sunday
succ(monday) = tuesday
3. 使用
枚举类型不能直接read和write,可用其序号与CASE结合使用。
子界
1. 定义
TYPE
<子界类型标识符> = <常量1>..<常量2>;
VAR
<子界类型变量表>: <子界类型标识符>;
- 子界只能是有序类型(整型,字符型,布尔型,枚举型),不能为实型。
- 程序执行时会先检查存储在子界类型变量中的值是否符合其定义要求,若错误,会停止程序打出错误信息。
枚举型的子界:
TYPE
day = (sun, mon, tue, wed, thu, fri, sat);
schoolday = mon..fri;
2. 运算
和其基类型运算一样,只是限制了自身的值的范围。
数组类型
一维数组
1. 定义
TYPE
<数组类型标识符> = array[<下标类型> of <基类型>];
VAR
<数组变量表>: <数组类型标识符>;
- 与c不同,下标从1开始。
- 值的表示和c语言相同,a[1]表示数组a的第1个元素值。
- 下标类型可为:布尔,字符,枚举,子界(1..10属于子界类型而不是整型)。
- 基类型可以是任何标准类型或用户定义类型,包括数组类型(字符串数组)。
例:day为枚举类型,数组类型art1由7个整型元素组成,art2由24个布尔类型元素组成。
TYPE
day = (sun, mon, tue, wed, thu, fri, sat);
art1 = array[day]of integer;
art2 = array['a'..'z']of boolean;
VAR
c: art1;
d: art2;
2. 使用
上述实例中数组类型art1值可能的对应关系如下:
c[sun] | c[mon] | c[tue] | c[wed] | c[thu] | c[fri] | c[sat] |
23 | 5 | 3454 | 33 | 57 | 24 | 543 |
基本与c语言相同,以下2句效果等价:
{a, b为同类型的数组,下标1-10}
b := a;
for i:=1 to 10 do
b[i] := a[i];
多维数组
1. 定义
TYPE
<数组类型标识符> = array[<下标类型1>, ..., <下标类型n>]of <基类型>;
例:
TYPE
mat = array[1..5, 1..10]of real;
VAR
a, b: mat;
2. 使用
和c相同,a[i][j] 表示上述二维数组的某一元素。
紧缩字符数组
当数组元素为字符型和布尔型时,按上述定义方式每个字节占用一个存储单元,假设每个存储单元由32个二进位构成,则会造成空间浪费。因为字符型仅需8个二进位,布尔型为1个二进位。
1. 定义:
VAR
a: array[1..10]of char;
b: packed array[1..10]of char;
a为一般字符数组,b为紧缩字符数组。
2. 紧缩字符数组相当于一个字符串,可以执行整体读语句:
readln(b);
输入:abcdefg
结果:b为(abcdefg ),结尾补3个空格
输入:abcdefghijk
结果:b为(abcdefghij),超出定义长度,多余部分去除
3. 对b进行整体赋值和比较操作要求长度相同:
b := 'abcdefj'; {错误}
b := 'abcdefg '; {正确}
4. 通常在存储字符数组和处理字符串时用b的形式以节约存储空间,而在处理每个字符时用a的形式以节约访问时间。pascal提供了2个标准过程用于a和b之间的转换:
{将非紧缩数组a的第n个下标开始的元素存储到紧缩数组b中,存储的元素个数为b的长度}
pack(a, n, b);
{将b的所有元素存储到a中,存储从a的第n个位置开始}
unpack(b, a, n);
通常上述的 n 写为 1。
保形数组参数
在数组作为参数时,实在参数和形式参数的下标的上下界可以不一致,实在参数不得超过形式参数所允许的下标界限。
VAR
a: array[1..4, 1..4]of real;
b: array[1..6, 1..6]of real;
PROCEDURE pro(var a:array[l1..u1:integer; l2..u2:integer]of real);
...
过程pro可以以数组a, b为实际参数进行运行,即使a, b的下标界限不相同。