子程式(過程、函式、方法)
一般程式設計語言包含兩種基本的抽象:過程抽象和資料抽象。過程抽象有時也稱控制抽象。
子程式在1950年以前就發明了,作為一種抽象那時候並未被完全接受。相反,最初它被看做是一種節省程式碼的機制,但很快子程式就被認可為過程抽象的一種方式。意識到子程式可以作為一種抽象機制,這產生了三個重要結果。
- 人們發明了一些語言,支援各種引數傳遞機制
- 奠定了「結構化程式設計」的基礎,語言開始支援巢狀的子程式(如JS的function,Java的inner class)
- 誕生了「結構化程式設計」,為試圖構建大型系統提供了指導,利用子程式作為基本構建塊
子程式是最主要過程抽象機制。面嚮物件語言中的方法與子程式的概念十分相似,不同在於它們的呼叫以及它們與類或物件關聯的方式。
一、子程式的特徵
- 每個子程式只有一個入口
- 在執行期間,呼叫程式單位被停止執行,即任意時刻只有一個子程式在執行
- 子程式執行完畢後,總是將控制返回給呼叫程式
注:協同程式 和 併發程式 的特徵與子程式不同。
二、兩種型別的子程式
子程式分為兩種型別:過程 和 函式。
過程 是定義引數化技術的語句系列,通過單個呼叫語句來啟動這些計算。過程實際上是定義了新的語句。在Ada中將過程稱為procedures,但在Fortran中則將過程稱為subroutines。
函式 在結構上模仿了過程,但在語義上卻模仿了數學中的函式。如果它是一個純函式,就不會有副作用。
注:某些程式設計語義同時提供了過程和函式,如Fortain和Ada;某些如基於C的語言則只有函式。但C中函式的行為卻與過程相似,也可以定義這些函式不返回任何值,只要將它們的返回型別定義為void。Java/C++/C#類似。
三、子程式的首部定義
例如定義一個sum的子程式
Fortran
1 |
Subroutine sum (parameters)
|
Ada
1 |
procedure sum (parameters)
|
Python
1 |
def sum (parameters)
|
C/C++/Java
1 |
int sum(parameters)
|
JavaScript
1 |
function sum(parameters)
|
Ruby/JavaScript的函式與以上存在著一些有趣的不同之處。既可以在類定義時定義,也可以在類外定義。在類外定義的被認為是呼叫物件或根物件的方法。Ruby中如果return語句後沒有表示式則返回nil,JavaScript中如果沒有return則返回undefined。
四、引數
子程式通常描述的是計算,它通過兩種方式來處理資料:
- 通過對非區域性變數的直接訪問
- 通過引數傳遞
前面已經提到過,純函式只通過引數傳遞來計算。非純函式是有 副作用 的。引數又分為形參和實參。
形參:子程式首部中定義的引數稱為形參,又被稱為 虛變數。因為它不是平常意義的變數,只有當子程式呼叫時它們才與儲存空間 繫結。
實參:子程式呼叫語句包括子程式的名稱及一組將子程式的形參繫結的引數,這些引數稱為實參。
多數語言的實參與形參的繫結(關聯) 是按位置進行的。即第一個實參對應於第一個形參,第二個實參對應於第二個形參。依次類推。如JavaScript程式碼
function sum(a,
b) {
return a
+ b;
}
sum(3,
5);
|
函式sum定義了兩個形參a, b。呼叫時對應的實參是3, 5。
使用位置將實參與形參關聯起來是是十分有效且安全的方式。但當引數很多時(形參列表很長比如有10個以上引數),程式設計師很容易在呼叫子程式時在實參的次序上犯錯。因此,有些語言中推出瞭解決方法稱為關鍵字引數。
關鍵字引數 的意義是將形參名稱與實參在一起宣告。即在宣告時就與實參一一對應了,它的好處是能以任何順序出現在實參表中。如Python程式碼
1 2 3 |
def sum ( len = myLen,
list = myList,
result = myResult)
|
在函式定義時就將引數與變數一一對應。使用關鍵字引數的缺點是子程式的必須知道形參的名稱。
引數預設值 在Python、Ruby、C++、Fortran 95、Ada和PHP中,形參可以有預設值。如果沒有將實參傳遞給子程式的形參,那麼將使用預設值。如Python
1 2 3 4 |
def show(a = 5 ):
print (a)
show()
|
定義函式show時形參a具有預設值5,呼叫show時如果沒有傳實參,那麼預設打印出5。
注:Javascript之父Brendan Eich準備給該語言新增該項特性。Javascript中函式的定義有可能是如下形式:
1 2 3 |
function fun(x=5,y=10){
}
|
在多數語言中形參不具備預設值,呼叫時實參個數必須和形參匹配一致。但在C、C++、Perl和JavaScript中,則沒有該項要求。雖然允許引數數目不同的設計容易引起錯誤,但有時也是很靈活方便的。例如C的printf函式就以列印任何數目的項。JavaScript也可以根據引數數目不同模擬函式的過載。
相關: