共用方式為


參數和引數 (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

請參閱

其他資源

成員 (F#)