參數 (程式設計)在程式設計中,參數(parameter)又稱形式引數(formal argument),是一種在调用子程序时用以向子程序传递資料的特殊变量,這些被傳遞資料也就是子程序引數(arguments)的值。[1][2][3]參數的有序列表通常包含在子例程的定義中,因此在每次调用子例程時也會計算這些傳入的引數,並且將對應資料送到子程序中。 不同於在數學中通常所使用的引數,計算機科學中的引數是在引動過程(invocation)或者呼叫陳述式(call statement)中傳遞給函式(function)、程序(procedure)或者常式(routine)的實際輸入表達式,而參數是子程序實現內部的變量。例如,定義一個 在最常見的傳值調用情況下,參數會在子程序中充當新的局部變量,並初始化為引數的值(如果引數是變量,則為引數的局部(隔離)副本)。但在例如傳引用調用的其他情況下,調用者提供的引數變量可能會受到被調用子例程中操作的影響。 所使用的語言一般定義了如何聲明參數以及將引數(的值)傳遞給子例程的參數的語義,但是在任何特定計算機系統中如何表示該參數的細節取決於該系統的調用約定。 示例
以下使用C語言編寫的程序定義了一個名為 double SalesTax( double price ){
return 0.05 * price;
}
定義函數後,可以按以下方式調用它: SalesTax(10.00);
示例中使用了引數 參數和引數術語參數和引數在不同的編程語言中可能會具有不同的含義。有時它們可以互換使用,並且以上下文區分含義。術語參數(也稱為形式參數(形參,formal parameter))通常用來指該變量作為在函數定義中變量的內容。而引數(也稱為實際參數(實參,actual parameter))是指在函數調用中提供的實際輸入。例如,如果將一個函數定義為 參數是一個(未綁定實際值的)變量,而引數可以是一個值或變量,或者是涉及值和變量的更複雜的表達式。在由值呼叫的情況下,所傳遞給函數是自變量的值。例如 一般而言,參數出現在過程定義中,引數則出現在過程調用中。在函數 參數是過程的固有屬性,包含在其定義中。例如,在許多語言中,將兩個提供的整數加在一起併計算總和的過程將需要兩個參數,每個整數一個。通常,可以使用任意數量的參數或完全不使用參數來定義過程。如果過程具有參數,則其定義中指定參數的部分稱為其參數列表(parameter list)。 相反,引數是調用過程時提供給過程的表達式,[5]其通常是一個與參數之一匹配的表達式。與參數(它們構成過程定義的不變部分)不同,自變量在調用之間可能有所不同。每次調用過程時,過程調用中指定引數的部分稱為引數列表(argument list)。 儘管參數通常也被混稱為引數,但是在運行時調用子例程時,有時會將引數視為分配給參數變量的實際值或引用。在討論正在調用子例程的程式碼時,傳遞給子例程的任何值或引用都是引數。
在討論子例程定義中的程式碼時,子例程的參數列表中的變量是參數,而運行時參數的值則會是引數。例如,在C語言中,處理線程時通常會傳入 void ThreadFunction( void* pThreadArgument )
{
//將第一個參數命名為pThreadArgument而不是pThreadParameter是正確的。
//在運行時,我們使用的值是一個引數。
//如上所述,在討論子例程定義時保留術語參數。
}
為了更好地理解它們之間的區別,請考慮以下用C語言編寫的函數: int Sum( int addend1, int addend2 )
{
return addend1 + addend2;
}
函數 int value1 = 40;
int value2 = 2;
int sum_value = Sum(value1, value2);
變量 在運行時,分配給這些變量的值作為參數傳遞給函數 由於參數和引數之間的差異,有可能向過程提供不合適的引數,也可能提供過多或過少的引數,要麼一個或多個引數可能是錯誤的類型,或者是以錯誤的順序提供了引數。這些情況中的任何一種都會導致參數列表和引數列表之間不匹配,並且該過程通常會返回意外的答案或生成執行期錯誤。 數據類型在強類型編程語言中,必須在過程聲明中指定每個參數的類型。使用類型推斷的語言會嘗試從函數的主體和用法中自動推斷類型。動態類型的編程語言則會將類型解析推遲到運行時。弱類型語言幾乎沒有類型解析,甚至依靠程序員來確保正確性。 一些語言使用特殊的關鍵字(例如 引數傳遞將參數分配給參數的確切機制稱為參數傳遞(argument passing),該機制取決於可以使用關鍵字來指定的參數求值策略(通常為按值傳遞)。 預設參數某些編程語言(例如Ada,C++,Clojure,Common Lisp,Fortran 90,Python,Ruby,Tcl和PowerShell)允許在子例程的聲明中顯式或隱式給出缺省参数。這允許調用者在調用子例程時忽略該參數。如果顯式給出了預設參數,則如果調用者未提供該值,則使用該值。如果預設參數是隱式的(有時通過使用諸如 PowerShell示例: function doc($g = 1.21) {
"$g gigawatts? $g gigawatts? Great Scott!"
}
PS > doc
1.21 gigawatts? 1.21 gigawatts? Great Scott!
PS > doc 88
88 gigawatts? 88 gigawatts? Great Scott!
預設參數可以看作是可變長度引數列表的一種特殊情況。 可變長度參數列表某些語言允許將子例程定義為接受可變數量的引數。對於此類語言,子例程必須遍歷參數列表。 PowerShell示例: function marty {
$args | foreach { "back to the year $_" }
}
PS > marty 1985
back to the year 1985
PS > marty 2015 1985 1955
back to the year 2015
back to the year 1985
back to the year 1955
命名參數某些編程語言(例如Ada和Windows PowerShell)許子例程具有命名參數。這使調用代碼更具自記錄性。它還為調用者提供了更大的靈活性,通常允許更改參數的順序,或者根據需要省略參數。 PowerShell示例: function jennifer($adjectiveYoung, $adjectiveOld) {
"Young Jennifer: I'm $adjectiveYoung!"
"Old Jennifer: I'm $adjectiveOld!"
}
PS > jennifer 'fresh' 'experienced'
Young Jennifer: I'm fresh!
Old Jennifer: I'm experienced!
PS > jennifer -adjectiveOld 'experienced' -adjectiveYoung 'fresh'
Young Jennifer: I'm fresh!
Old Jennifer: I'm experienced!
函式語言中的多個參數在lambda演算中,每個函數只有一個參數。通常認為具有多個參數的函數在lambda演算中表示為具有第一個引數的函數,並返回具有其餘引數的函數。這種轉換也被稱為柯里化(Currying)。一些編程語言(例如ML和Haskell)遵循此方案。在這些語言中,每個函數都只有一個參數,看起來像多個參數的函數的定義,實際上是返回一個函數的函數的定義的語法糖等。在這些語言以及lambda演算中的函數應用是左關聯的,因此,這種操作看起來會像是將函數應用於多個引數的函數先將函數應用於第一個引數,然後將結果函數應用於第二個自變量,並依此類推。 輸出參數輸出參數,也稱為出參或返回參數,是一種用於輸出而不是更通常的使用用於輸入的參數。在某些語言中,尤其是C和C ++,輸出參數是一種習慣用法,而其他語言則內置了對輸出參數的支持。內置支持輸出參數的語言包括Ada[6]、Fortran、SQL的各種拓展程式(例如:PL/SQL[7]和Transact-SQL)、C#[8]和.NET框架[9]、Swift語言[10]以及手稿語言TScript。 更準確地說,參數模式或參數共有三種類型:輸入參數(入參)、輸出參數(出參)、輸入輸出參數(出入參),這些參數往往使用 在某些情況下,只有輸入和輸入輸出是有區別的,而輸出則被視為輸入/輸出的特定用途,而在其他情況下,僅支持輸入和輸出(但不支持輸入輸出)。默認模式因語言而異:在Fortran 90中,默認輸入/輸出,在C#和SQL擴展中,默認輸入/輸出,在TScript中,每個參數都明確指定為輸入或輸出。
從語法上講,參數模式通常在函數聲明(例如在C#中: TextExtent(WString text, Font font : Integer width, Integer height)
參數模式是指稱語義的一種形式,其表明了程式設計師的意圖並允許編譯器捕獲錯誤來應用優化,它們不一定暗示操作語義(參數傳遞的實際發生方式)。值得注意的是,雖然輸入參數可以通過值調用實現,而輸出和輸入輸出參數可以通過引用調用實現,這是在沒有內置支持的情況下以語言實現這些模式的直接方法,但也並非總是如此實施。《Ada '83基本原理(英語:Ada '83 Rationale)》中詳細討論了這種區別,其中強調了參數模式是從中實際實現的參數傳遞機制(通過引用或通過複製)抽像出來的。[6]例如,在C#中,按值傳遞輸入參數(默認,無關鍵字),而按引用傳遞輸出和輸入/輸出參數( function f(x, y: integer): integer;
begin
f := x + y;
end;
這在語義上是不同的,因為在調用時僅對函數進行求值–不會從調用範圍傳遞變數來存儲輸出。 使用輸出參數的主要用途是從一個函數返回多個值,而輸入/輸出參數的用途是使用參數傳遞(而不是像全局變量那樣在共享環境中)來修改狀態。 返回多個值的一個重要用途是解決返回值和錯誤狀態的半謂詞問題。 例如,要從C函數中返回兩個變數,可以寫: int width
int height;
F(x, &width, &height);
其中 public static bool TryParse(string s, out int result)
可以這樣使用: int result;
if (!Int32.TryParse(s, result)) {
// exception handling
}
類似的考慮適用於返回幾種可能類型之一的值,其中返回值可以指定類型,然後將值存儲在幾種輸出變量之一中。 缺點在現代編程中,通常不鼓勵使用輸出參數,因為它們笨拙、令人困惑且級別太低,普通的返回值相當容易理解和使用。[12]值得注意的是,輸出參數涉及具有副作用(修改輸出參數)的函數,並且在語義上與引用相似,比純函數和值更容易混淆,並且輸出參數與輸入/輸出參數之間的區別可能微妙。 此外,由於在通常的編程樣式中,大多數參數只是輸入參數,因此輸出參數和輸入輸出參數是異常的,從而容易引起誤解。 輸出和輸入輸出參數會阻止函數組合,因為輸出存儲在變量中,而不是表達式的值中。 因此,必須首先聲明一個變量,然後函數鏈的每一步都必須是一個單獨的語句。 例如,在C ++中,以下函數組成: Object obj = G(y, F(x));
當寫有輸出和輸入輸出參數時,可以寫為(對於 Object obj;
F(x, &obj);
G(y, &obj);
在具有單個輸出或輸入/輸出參數且沒有返回值的函數的特殊情況下,如果該函數還返回輸出或輸入輸出參數(或在C / C ++中是其地址),則可以構成函數,在這種情況下,以上內容將變為: Object obj;
G(y, F(x, &obj));
替代方式輸出參數的用例有多種選擇。 為了從一個函數返回多個值,一種替代方法是返回一個元組 。 從語法上講,如果可以使用自動序列拆包和並行賦值 (例如在Go或Python中),這會更清楚: def f():
return 1, 2
a, b = f()
為了返回幾種類型之一的值,可以使用標籤聯合 。最常見的情況是可空類型 ( 可選型別 ),其中返回值可以為 result = Parse(s)
if result is None:
# exception handling
或者,更慣用的方法: try:
result = Parse(s)
except ParseError:
# exception handling
不需要局部變量並在使用輸出變量時復制返回值的微優化也可以通過足夠複雜的編譯器應用於常規函數和返回值。[8] 使用C和相關語言輸出參數的通常替代方法是返回包含所有返回值的單個數據結構。 例如,給定一個封裝寬度和高度的結構,可以這樣寫: WidthHeight width_and_height = F(x);
在面向對象的語言中,通常不使用輸入輸出參數,而是通過传共享对象调用,將引用傳遞給對象然後對對象進行突變來使用調用 ,而不更改變量所引用的對象。[12] 相關條目參考資料
|
Portal di Ensiklopedia Dunia