zoukankan      html  css  js  c++  java
  • Object Pascal 过程与函数

    过程与函数

    过程与函数是实现一定功能的语句块,是程序中的特定功能单元。可以在程序的其他地方被调用,也可以进行递归调用。过程与函数的区别在于过程没有返回值,而函数有返回值。


    1.过程与函数的定义

    过程与函数的定义包括过程原型或函数原型、过程体或函数体的定义。过程定义的形式如下:

    procedure ProcedureName(ParameterList); directives;
    var
        LocalDeclarations;
    begin
        statements
    end;

    ProcedureName 是过程名,是有效的标识符。ParameterList 为过程的参数列表,需要指明参数的个数和数据类型。Directives 是一些关于函数的指令字, 如果设置多个, 应该用分号隔开。LocalDeclarations 中定义了该函数中需要使用的一些临时变量,通常也称作本地变量。在Begin 与End 之间是过程调用时实现特定功能的一系列语句。ParameterList、Directives、LocalDeclarations 和Statements 都是可选部分。

    函数的定义与过程非常类似,只是使用的保留字不同,而且多了一个返回值类型。具体形式如下:

    function FunctionName(ParameterList): ReturnType; directives;
    var
        LocalDeclarations;
    begin
        statements
    end;

    可以将函数需要返回的数值赋值给变量Result。如果函数体中存在着一些由于判断而产生的分支语句时,就要在每一个分支中设置返回值。通常要根据函数的返回值来确定下一步的操作。注意,这里与Visual C 和Visual C++不一样,把一个值赋给Result,函数并不会结束。


    2.参数

    函数定义时参数列表中的参数称为形参,函数调用时参数列表中的参数称为实参。在定义的函数原型中,多个参数之间用分号隔开,同一类型的参数可以放在一起,以逗号隔开。函数调用的时候,在函数原型中,多个参数之间用逗号隔开。
    一般来说,形参列表和实参列表完全匹配是指参数的个数一样,而且顺序排列的数据类型也完全一致。对于普通的函数,如果编译器发现实参的数据类型与形参的数据类型不匹配,会将实参的数据类型进行一次或多次的“提升”,比如把Integer 类型转换为Double 类型。可以为过程和函数的参数指定默认数值。指定默认数值的参数要放在参数列表的后部,而没有指定默认数值的参数要放在参数列表的前部。在函数调用的时候,可以为设置了默认值的参数指定一个新值。在函数体中,各语句使用的是指定的新值,如果没有指定新值,则使用默认值。同样,如果存在多个设置了默认值的参数,只有在前面的参数指定了新值后,后面的参数才可以指定新值。

    下面的例子定义了一个OutputNum 函数,可以将一个浮点数按指定的精度输出。通过这个例子,读者可以体会函数中参数的使用: 

    program Project1;
    {$APPTYPE CONSOLE}
    
    uses Sysutils; //为了使用函数Format
    
    function OutputNum(number:double;n:integer = 5):Boolean;
    var
        Str : string; //浮点数显示输出的内容
    
    begin
        if n <= -1 then //小数点后的位数要大于或等于0
            begin
                Result:=False;
                Exit; //退出显示函数
            end
        else
            begin
                // 设置显示的格式
                Str := Format('%*.*f', [10, n, number]);
                Result := True ;
                Writeln(Str); //显示数据
            end;
    end;
    
    begin
        OutputNum(12.345); //n 默认为5
        OutputNum(123,3); //参数对数据类型进行升级
        //下面一句代码不正确,故屏蔽掉
        //OutputNum(123.456789,9.13); //参数对数据类型不能降级
        //可以根据函数的返回值确定下一步的操作
        if OutputNum(123.456789,-3) = False then
            Writeln('输出失败。') ;
        Readln;
    end.

    运行结果如下:

    12.34500
    123.000
    输出失败。

    这里有几点需要说明:

    • 为了使用函数Format,需要在Uses 语句中将Sysutils 单元包含进去。
    • 由于小数点后的位数不可以设置为负数,所以当出现负数时,OutputNum 函数返回False,并调用Exit 函数立刻退出OutputNum 函数。
    • 在语句OutputNum(123,3)中,首先将整型常数123 转换为浮点型常数,然后进行参数传递。

    最常用的参数有3 种,分别为数值参数变量参数常量参数
    数值参数在运行过程中只改变其形参的值,不改变其实参的值,即参数的值不能传递到过程的外面。试看下面的例程:

    procedure Calculate(CalNo:Integer);
    begin
        CalNo := CalNo*10;
    end;

    用以下例程调用Calculate 函数:

    Calculate(Number);

    Number 进入Calculate 函数后,会把Number 实参拷贝给形参CalNo,在此过程中CalNo 增大10倍,但并未传递出来,所以说Number 值并未改变。形参和实参占用不同的内存地址,在过程或函数被调用时,将实参的值复制到形参占用的内存中。因此,在跳出过程或函数后,形参和实参的数值是不同的,但实参的值并不发生变化。

    如果想改变传入的参数值,就需要使用变量参数,即在被调用程序的参数表中的形参前加上保留字Var。例如:

    procedure Calculate(var CalNo : Integer);

    则CalNo 并不在内存中占据一个位置,而是指向实参Number。当一个实参被传递时,任何对形参所作的改变都会反映到实参中,这是因为两个参数指向同一个地址。将上一个例程中的形参CalNo前面加上Var,再以同样的程序调用它,则在第2 个编辑框中会显示计算的结果,把第1 个编辑框中的数值放大10 倍。这时形参CalNo 和实参Number 的值都是Nnmber 初始值的10 倍。

    如果过程或函数执行时要求不改变形参的值,最有保证的办法是使用常量参数。在参数表的参数名称前加上保留字Const 就可以使一个形参成为常量参数。使用常量参数代替数值参数可以保护用户的参数,使用户在不想改变参数值时不会意外地将新的值赋给这个参数。下面的例子可以帮助读者加深理解:

     
    program Project1;
    {$APPTYPE CONSOLE}
    
    type
        PInteger = ^Integer; //定义指针类型
    
    procedure P1(var N:Integer); //引用参数传递
    begin
        N:=N+1 ;
    end;
    
    procedure P2(N:Integer); //普通参数传递
    begin
        N:=N+2;
    end;
    
    procedure P3(PT:PInteger); //传递指针参数
    begin
        PT^:=PT^+3;
    end;
    
    var
        i:Integer;
    begin
        i:=1;
        P1(i); //将i 的值增加1
        Writeln('i:',i);
        P2(i); //希望将i 加2,但没有实现
        Writeln('i:',i);
        P3(@i); //将i 加3    
        Writeln('i:',i);
        Readln;
    end.

    运行结果如下:

    i:2
    i:2
    i:5

    这里有几点需要说明:

    • 一开始变量i 的数值为1,经过P1 过程的处理,将i 加1,所以显示的第1 个i 的数值为2,这时使用的是引用参数传递。
    • 在过程P2 中,将形参的数值增加了2,实际上i 并没有增加,所以显示的第2 个i 的数值仍然为2。在这种情况下,正常的做法可以使用函数的返回值,例如:
    Result:=N+2;

    在调用函数的时候使用:

    i:=P2(i);
    • 在过程P3 中,传递的是变量I 的指针,所以操作是针对i 进行的,第3 次显示i 的数值是5。


    3.过程与函数的调用约定
      在调用过程或函数的时候,如果参数列表中具有多个参数,那么参数传递给过程或函数的顺序会对结果产生一定的影响。对于不同的语言,参数传递的顺序是不同的:Pascal 语言是按照从左向右的顺序进行传递的,而C 语言是按照从右向左的顺序来传递的。

      为了确定传递的顺序,可以在过程或函数定义的时候,在Directives 部分利用指令字指定传递的顺序。来自Delphi 的联机帮助的数据,如表1-13 所示,其中列举了Directives 部分可使用的关于函数调用约定的指令字。
    表1-13 定义过程与函数时对调用约定起作用的指令字

    Directive 指令 Register Pascal Stdcall safecall Cdecl
    参数传递顺序 从左向右 从左向右 从右向左 从右向左 从右向左


    可以通过下面的例子查看参数传递的顺序:

    program Project1;
    {$APPTYPE CONSOLE}
    
    function P1:Integer; //该函数将作为GetMax 函数的第1 个参数
    begin
        Writeln('P1');
        Result:=0;
    end;
    
    function P2:Integer; //该函数将作为GetMax 函数的第2 个参数
    begin
        Writeln('P2 ') ;
        Result:=1;
    end;
    
    //参数的传递方式采用pascal 方式
    function GetMax(N1:Integer; N2:Integer):Integer;pascal;
    begin
        Result:=N1+N2;
    end;
    
    begin
        GetMax(P1,P2);
    end.

    运行结果如下:


    1 P1

    2 P2

    如果将GetMax 函数定义处的Directives 部分由Pascal 改为Stdcall,则运行结果变为:

    P2
    P1

    用户可以修改GetMax 函数定义处的Directives 部分为表1-13 中的其他数值,测试结果是否一致。

    4.过程和函数的重载

    可以在同一个作用范围内给不同的过程或函数取同一个名称,这种现象就叫做重载。这样可以使编程更方便。在重载的情况下,决定使用哪个过程或函数的依据是形参和实参的一致性,即参数个数、参数类型以及它们之间的顺序,不存在一个函数调用满足两个重载函数的情况。另外重载函数必须用指令字Overload 来进行说明,函数的返回值类型不同就不可以作为重载函数的依据。

    下面的两个函数就是重载函数:

    function Average(a:Integer; b:Integer):Double;overload; //求整形数据的平均值
    function Average(a:Double; b:Double):Double;overload; //求实数数据的平均值

    下面两条语句就调用了不同的函数:

    Average(3.7,4.6); //调用的是第2 个重载函数
    Average(3,4); //调用的是第1 个重载函数

    如果又定义了一个重载函数如下:

    function Average(a,b:Double;c:Double=0.0):Double;overload; //求3 个实数平均值

    从上例可以看出,尽管参数的个数与上面的两个不同,但第3 个参数设置了一个默认值,所以当参数调用为语句Average(1.1,2.2);时,编译系统就不知道应该使用哪个重载函数了,因为第2 个重载函数和第3 个重载函数都可以满足要求,这样就会出现一个编译错误。


    5.函数的递归调用

    在Object Pascal 中,过程或函数必须先说明再调用。以上规则在递归调用时属于例外情况。所谓递归调用,是指函数A 调用函数B,而函数B 又调用函数A 的情况,或是指一个函数调用自身的特殊情况。在递归调用中,函数要进行前置,即在函数或过程的标题部分最后加上保留字Forward。下文的例子是一个递归调用的典型例子:

    program Project1;
    {$APPTYPE CONSOLE}
    
    var
        Alpha:Integer;
    
    procedure Test2(var A:Integer);forward;
    
    //Test2 被说明为前置过程
    procedure Test1(var A:Integer);
    begin
        A:=A-1;
        if A>0 then
            Test2(A); //经前置说明,调用未执行的过程Test2
            writeln(A);
    end;
    
    procedure Test2(var A:Integer); //经前置说明的Test2 的执行部分
    begin
        A:=A div 2;
            if A>0 then
                Test1(A); //在Test2 中调用已执行的过程Test1
    end;
    
    begin
        Alpha := 15; //给Alpha 赋初值
        Test1(Alpha); //第1 次调用Test1,递归开始
    end.

    程序开始时给Alpha 赋初值,并实现先减1 再除2 的循环递归调用,直到Alpha 小于0 为止。

     

    规范化命名

    在系统开发的过程中,常常要为变量、类、对象、函数和文件等命名。一般在开发需求或设计阶段就必须制定出一套完整、实用的命名规则。这样,在很大程度上可以提高系统开发的效率,便于不同模块之间的接口,方便系统的维护。
    在制定命名规则的时候,一个基本的原则就是便于使用、便于维护、风格统一。另外还应注意下面几点:

    • 命名时要采用英文单词,而不要使用中文拼音,尤其不要使用中文拼音第1 个字母的组合。在使用英文单词命名时,尽量采用统一、简单、贴切的词语,尽可能使用完整的单词或音节。
    • 有些名称可以采用几个英文单词的组合。在组合过程中,尽量不要使用下划线来分隔单词,最好采用大小写混写的方式来实现。
    • 对于保留字和指令字可以统一全部小写,而对于一些常量名可以全部大写。
    • 有些名称可以是“动词+对象”组合而成,也可以是“对象+动词”组合而成。一般来说,“动词+对象”比较符合平常的语法习惯。无论采用哪种方法,整体上都应该统一。
    • 在有些情况下,要考虑到与Delphi 集成开发环境的统一。例如在Delphi 集成开发环境中,普通类的名称一般以T 开头,异常类的名称一般以E 开头。
    • 在对菜单命令的标识号命名的时候,应将所属菜单项的名称包含进去。比如对于“文件”菜单项中的菜单命令,可以将标识号命名为FileOpen、FileClose 等。
    • 对于一些表示集合意义的名称,可以使用名词的复数形式。比如窗口的集合,可以使用Windows,而不要使用WindowCollection。
  • 相关阅读:
    数据库连接池系列之——c3p0
    spring配置
    flume ng系列之——flume安装
    flume ng之组件介绍
    flume ng之TailSource
    Flume-NG + HDFS + HIVE日志收集分析
    flume+hadoop
    Flume+kafka+storm+hdfs
    IOS中坐标转换
    tabBarItem是模型,只有控件才有textColor属性
  • 原文地址:https://www.cnblogs.com/ivantang/p/3833695.html
Copyright © 2011-2022 走看看