過程與函數
過程與函數是實現一定功能的語句塊,是程序中的特定功能單元??梢栽诔绦虻钠渌胤奖徽{用,也可以進行遞歸調用。過程與函數的區(qū)別在于過程沒有返回值,而函數有返回值。
過程與函數的定義包括過程原型或函數原型、過程體或函數體的定義。過程定義的形式如下: 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,函數并不會結束。
函數定義時參數列表中的參數稱為形參,函數調用時參數列表中的參數稱為實參。在定義的函數原型中,多個參數之間用分號隔開,同一類型的參數可以放在一起,以逗號隔開。函數調用的時候,在函數原型中,多個參數之間用逗號隔開。 下面的例子定義了一個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 輸出失敗。
這里有幾點需要說明:
最常用的參數有3 種,分別為數值參數、變量參數和常量參數。 procedure Calculate(CalNo:Integer); begin CalNo := CalNo*10; end; 用以下例程調用Calculate 函數: Calculate(Number); Number 進入Calculate 函數后,會把Number 實參拷貝給形參CalNo,在此過程中CalNo 增大10倍,但并未傳遞出來,所以說Number 值并未改變。形參和實參占用不同的內存地址,在過程或函數被調用時,將實參的值復制到形參占用的內存中。因此,在跳出過程或函數后,形參和實參的數值是不同的,但實參的值并不發(fā)生變化。 如果想改變傳入的參數值,就需要使用變量參數,即在被調用程序的參數表中的形參前加上保留字Var。例如: procedure Calculate(var CalNo : Integer); 則CalNo 并不在內存中占據一個位置,而是指向實參Number。當一個實參被傳遞時,任何對形參所作的改變都會反映到實參中,這是因為兩個參數指向同一個地址。將上一個例程中的形參CalNo前面加上Var,再以同樣的程序調用它,則在第2 個編輯框中會顯示計算的結果,把第1 個編輯框中的數值放大10 倍。這時形參CalNo 和實參Number 的值都是Nnmber 初始值的10 倍。 如果過程或函數執(zhí)行時要求不改變形參的值,最有保證的辦法是使用常量參數。在參數表的參數名稱前加上保留字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 這里有幾點需要說明:
Result:=N+2; 在調用函數的時候使用: i:=P2(i);
為了確定傳遞的順序,可以在過程或函數定義的時候,在Directives 部分利用指令字指定傳遞的順序。來自Delphi 的聯機幫助的數據,如表1-13 所示,其中列舉了Directives 部分可使用的關于函數調用約定的指令字。
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,則運行結果變?yōu)椋?/p> 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 個重載函數都可以滿足要求,這樣就會出現一個編譯錯誤。
在Object Pascal 中,過程或函數必須先說明再調用。以上規(guī)則在遞歸調用時屬于例外情況。所謂遞歸調用,是指函數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); //經前置說明,調用未執(zhí)行的過程Test2 writeln(A); end; procedure Test2(var A:Integer); //經前置說明的Test2 的執(zhí)行部分 begin A:=A div 2; if A>0 then Test1(A); //在Test2 中調用已執(zhí)行的過程Test1 end; begin Alpha := 15; //給Alpha 賦初值 Test1(Alpha); //第1 次調用Test1,遞歸開始 end. 程序開始時給Alpha 賦初值,并實現先減1 再除2 的循環(huán)遞歸調用,直到Alpha 小于0 為止。
規(guī)范化命名
在系統開發(fā)的過程中,常常要為變量、類、對象、函數和文件等命名。一般在開發(fā)需求或設計階段就必須制定出一套完整、實用的命名規(guī)則。這樣,在很大程度上可以提高系統開發(fā)的效率,便于不同模塊之間的接口,方便系統的維護。
|
|