參數和引數 (F#)
本主題描述對定義參數以及將引數傳遞至函式、方法和屬性的語言支援, 它包括如何以傳址方式傳遞的資訊,以及如何定義和使用可以採用變數引數數目的方法。
參數和引數
「參數」(Parameter) 描述必須提供之值的名稱。 「引數」(Argument) 用於提供給每個參數的值。
參數可以用 Tuple 或局部調用形式,或用兩者的某些組合來指定。 您可以使用明確的參數名稱來傳遞引數。 方法的參數可以指定為選擇性並設定預設值。
參數模式
提供給函式和方法的參數通常是以空格分隔的模式。 這表示,原則上搜尋運算式 (F#)中描述的任何模式都可以用於函式或成員的參數清單中。
方法通常使用 Tuple 形式來傳遞引數。 比起其他 .NET 語言,這會得到更清楚的結果,因為 Tuple 形式符合 .NET 方法中傳遞引數的方式。
局部調用形式最常用於透過 let 繫結所建立的函式。
下列虛擬程式碼會示範 Tuple 和局部調用引數的範例。
// Tuple form.
member this.SomeMethod(param1, param2) = ...
// Curried form.
let function1 param1 param2 = ...
當有些引數為 Tuple 形式而有些並不是時,可以使用組合形式。
let function2 param1 (param2a, param2b) param3 = ...
其他模式也可以用於參數清單中,但是如果參數模式不符合所有可能的輸入,在執行階段可能會有不完整符合的情形。 當引數的值不符合參數清單中指定的模式時,會產生例外狀況 MatchFailureException。 當參數模式允許不完整的符合結果時,編譯器會發出警告。 至少有另一個模式常用於參數清單,那就是萬用字元模式。 當您要忽略任何提供的引數時,可在參數清單中使用萬用字元模式。 在下列程式碼中,會示範引數清單中萬用字元模式的用法。
let makeList _ = [ for i in 1 .. 100 -> i * i ]
// The arguments 100 and 200 are ignored.
let list1 = makeList 100
let list2 = makeList 200
當您不需要傳入的引數時,例如在程式的主要進入點中,對一般做為字串陣列所提供的命令列引數不感興趣時,萬用字元模式會很有用,如下列程式碼所示。
[<EntryPoint>]
let main _ =
printfn "Entry point!"
0
引數中有時候會使用的其他模式為 as 模式、與已區分聯集關聯的識別項模式以及作用中模式。 您可以依照下列方式,使用單一案例已區分聯集模式。
type Slice = Slice of int * int * string
let GetSubstring1 (Slice(p0, p1, text)) =
printfn "Data begins at %d and ends at %d in string %s" p0 p1 text
text.[p0..p1]
let substring = GetSubstring1 (Slice(0, 4, "Et tu, Brute?"))
printfn "Substring: %s" substring
輸出如下。
Data begins at 0 and ends at 4 in string Et tu, Brute?
Et tu
使用中的模式時是很有用做為參數,例如,將引數轉換為所要的格式,如下例所示:
type Point = { x : float; y : float }
let (| Polar |) { x = x; y = y} =
( sqrt (x*x + y*y), System.Math.Atan (y/ x) )
let radius (Polar(r, _)) = r
let angle (Polar(_, theta)) = theta
您可以使用 as 模式將相符值儲存為區域值,如下列程式碼所示。
let GetSubstring2 (Slice(p0, p1, text) as s) = s
另一個偶爾使用的模式為函式,它會透過提供 Lambda 運算式做為函式主體,以便將最後一個引數保留為不具名,而這個運算式會立即針對隱含引數執行模式比對。 下列程式碼就是這樣的範例。
let isNil = function [] -> true | _::_ -> false
這個程式碼會定義接受泛型清單的函式,如果清單為空白則傳回 true,否則傳回 false。 使用這種技術會使程式碼比較難閱讀。
涉及不完整符合結果的模式偶爾會有用,例如,如果您知道程式中的清單只有三個項目,可以在參數清單中使用如下的模式。
let sum [a; b; c;] = a + b + c
有不完整符合結果的模式用法,最好在需要快速原型處理和其他臨時用途時才使用。 編譯器將對這類程式碼發出警告。 這類模式無法涵蓋所有可能輸入的一般案例,因此不適合用於元件 API。
具名引數
方法的引數可以依照逗號分隔之引數清單中的位置來指定,或者提供後面接著等號和要傳入之值的名稱,藉此明確傳遞給方法。 如果藉由提供名稱來指定,引數出現順序可以不同於宣告中的使用順序。
具名引數可使程式碼更容易閱讀,也比較能適應 API 中的某些變更類型,例如重新調整方法參數的順序。
只有方法允許具名引數,而使用 let 繫結的函式、函式值或 Lambda 運算式並不允許具名引數。
在下列程式碼範例中,會示範具名引數的用法。
type SpeedingTicket() =
member this.GetMPHOver(speed: int, limit: int) = speed - limit
let CalculateFine (ticket : SpeedingTicket) =
let delta = ticket.GetMPHOver(limit = 55, speed = 70)
if delta < 20 then 50.0 else 100.0
let ticket1 : SpeedingTicket = SpeedingTicket()
printfn "%f" (CalculateFine ticket1)
在類別建構函式呼叫,您可以使用類似具名引數的語法設定類別的屬性值。 以下範例說明此語法。
type Account() =
let mutable balance = 0.0
let mutable number = 0
let mutable firstName = ""
let mutable lastName = ""
member this.AccountNumber
with get() = number
and set(value) = number <- value
member this.FirstName
with get() = firstName
and set(value) = firstName <- value
member this.LastName
with get() = lastName
and set(value) = lastName <- value
member this.Balance
with get() = balance
and set(value) = balance <- value
member this.Deposit(amount: float) = this.Balance <- this.Balance + amount
member this.Withdraw(amount: float) = this.Balance <- this.Balance - amount
let account1 = new Account(AccountNumber=8782108,
FirstName="Darren", LastName="Parker",
Balance=1543.33)
如需詳細資訊,請參閱建構函式 (F#)。
選擇性參數
您可以在參數名稱前面使用問號,指定方法的選擇性參數。 選擇性參數會被解譯為 F# 選項型別,因此您可以使用一般查詢選項型別的方式,以 match 運算式搭配 Some 和 None 來查詢選擇性參數。 只有成員允許選擇性參數,使用 let 繫結建立的函式並不允許選擇性參數。
您也可以使用 defaultArg 函式,設定選擇性引數的預設值。 defaultArg 函式接受選擇性參數做為第一個引數,並接受預設值做為第二個引數。
在下列範例中,會示範選擇性參數的用法。
type DuplexType =
| Full
| Half
type Connection(?rate0 : int, ?duplex0 : DuplexType, ?parity0 : bool) =
let duplex = defaultArg duplex0 Full
let parity = defaultArg parity0 false
let mutable rate = match rate0 with
| Some rate1 -> rate1
| None -> match duplex with
| Full -> 9600
| Half -> 4800
do printfn "Baud Rate: %d Duplex: %A Parity: %b" rate duplex parity
let conn1 = Connection(duplex0 = Full)
let conn2 = Connection(duplex0 = Half)
let conn3 = Connection(300, Half, true)
輸出如下。
Baud Rate: 9600 Duplex: Full Parity: false
Baud Rate: 4800 Duplex: Half Parity: false
Baud Rate: 300 Duplex: Half Parity: true
以傳址方式傳遞
F# 支援 byref 關鍵字,該關鍵字指定參數是以傳址方式傳遞。 這表示對值所做的任何變更,在函式執行後都會繼續保留。 提供給 byref 參數的值必須是可變的。 或者,您可以傳遞適當型別的參考儲存格。
.NET 語言中以傳址方式傳遞是為了從函式傳回多個值而逐步形成的。 在 F# 中,您可以傳回 Tuple 以達到相同的目的,或使用可變動的參考儲存格做為參數。 byref 參數主要是做為與 .NET 程式庫互通之用。
在下列範例中,會示範 byref 關鍵字的用法。 請注意,當您使用參考儲存格做為參數時,必須將參考儲存格建立為具名值並做為參數,而不只是加入 ref 運算子,如下列程式碼中對 Increment 的第一個呼叫所示。 因為建立參考儲存格即會建立基礎值的複本,所以第一個呼叫只會遞增暫存值。
type Incrementor(z) =
member this.Increment(i : int byref) =
i <- i + z
let incrementor = new Incrementor(1)
let mutable x = 10
// Not recommended: Does not actually increment the variable.
incrementor.Increment(ref x)
// Prints 10.
printfn "%d" x
let mutable y = 10
incrementor.Increment(&y)
// Prints 11.
printfn "%d" y
let refInt = ref 10
incrementor.Increment(refInt)
// Prints 11.
printfn "%d" !refInt
您可以使用 Tuple 做為傳回值,在 .NET 程式庫方法中儲存任何 out 參數。 或者,您可以將 out 參數視為 byref 參數。 下列程式碼範例說明這兩個方式。
// TryParse has a second parameter that is an out parameter
// of type System.DateTime.
let (b, dt) = System.DateTime.TryParse("12-20-04 12:21:00")
printfn "%b %A" b dt
// The same call, using an address of operator.
let mutable dt2 = System.DateTime.Now
let b2 = System.DateTime.TryParse("12-20-04 12:21:00", &dt2)
printfn "%b %A" b2 dt2
參數陣列
您有時必須定義會接受任意數目之異質型別參數的函式。 為考量所有可用的型別而建立所有可能的多載方法,這種方式並不實際。 .NET 平台可透過參數陣列功能提供這類方法的支援。 在本身之簽章中接受參數陣列的方法可以接受任意數目的參數。 參數是放在陣列中。 陣列元素的型別決定可傳遞至函式的參數型別。 如果您以 Object 做為元素型別來定義參數陣列,用戶端程式碼就會傳遞任何型別的值。
在 F# 中,參數陣列只能在方法中定義, 而不能用於獨立函式或模組中定義的函式。
您可以使用 ParamArray 屬性來定義參數陣列。 ParamArray 屬性只能套用至最後一個參數。
下列程式碼說明如何呼叫會接受參數陣列的 .NET 方法,以及如何定義 F# 型別,定義中包含會接受參數陣列的方法。
open System
type X() =
member this.F([<ParamArray>] args: Object[]) =
for arg in args do
printfn "%A" arg
[<EntryPoint>]
let main _ =
// call a .NET method that takes a parameter array, passing values of various types
Console.WriteLine("a {0} {1} {2} {3} {4}", 1, 10.0, "Hello world", 1u, true)
let xobj = new X()
// call an F# method that takes a parameter array, passing values of various types
xobj.F("a", 1, 10.0, "Hello world", 1u, true)
0
在專案中執行時,上述程式碼的輸出如下:
a 1 10 Hello world 1 True
"a"
1
10.0
"Hello world"
1u
true