パラメーターと引数 (F#)
このトピックでは、パラメーターを定義したり、関数、メソッド、およびプロパティに引数を渡したりするための言語サポートについて説明します。 参照渡しの方法と、可変個の引数を受け取ることのできるメソッドを定義して使用する方法についての情報が含まれます。
パラメーターと引数
パラメーターという用語は、入力されるはずの値の名前を表すのに使用します。 引数という用語は、各パラメーターに入力される値に対して使用します。
パラメーターは、タプル形式かカリー化形式、またはその 2 つの組み合わせで指定できます。 引数は、明示的なパラメーター名を使用して渡すことができます。 メソッドのパラメーターをオプションとして指定し、既定値を設定することもできます。
パラメーター パターン
関数やメソッドに指定されるパラメーターは、通常、スペースで区切られたパターンです。 つまり、原則として、関数またはメンバーのパラメーター リストでは、「match 式 (F#)」で説明されているあらゆるパターンを使用できます。
メソッドでは、通常、タプル形式を使用して引数を渡します。 タプル形式は .NET メソッドで引数が渡される方法に適合するため、他の .NET 言語と比較して、結果がより明確になります。
カリー化形式は、ほとんどの場合、let 束縛を使用して作成された関数と共に使用します。
タプル形式とカリー化形式の引数の例を次の擬似コードに示します。
// Tuple form.
member this.SomeMethod(param1, param2) = ...
// Curried form.
let function1 param1 param2 = ...
タプルになった引数とそうでない引数がある場合は、形式を組み合わせて使用することもできます。
let function2 param1 (param2a, param2b) param3 = ...
その他のパターンもパラメーター リストで使用できますが、入力される可能性のある値にパラメーター パターンと一致しないものがある場合は、実行時に不完全な一致が発生することがあります。 引数の値が、パラメーター リストで指定されたパターンに一致しない場合は、例外 MatchFailureException が生成されます。 パラメーター パターンで不完全な一致を許容すると、コンパイラから警告が発行されます。 一般に、パラメーター リストで使用すると便利な他のパターンが、少なくとも 1 つあります。それはワイルドカード パターンです。 入力される引数を無視する必要がある場合は、パラメーター リストでワイルドカード パターンを使用します。 引数リストでワイルドカード パターンを使用するコード例を次に示します。
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
使用されることがあるもう 1 つのパターンは、暗黙の引数に対してパターン一致を直ちに実行するラムダ式を、関数の本体として提供して、最後の引数を名前のないままにしておく関数です。 この例を次のコード行に示します。
let isNil = function [] -> true | _::_ -> false
このコードで定義している関数は、ジェネリック リストを受け取り、リストが空の場合は true を、それ以外の場合は false を返します。 このような手法を使用すると、コードが読みにくくなる場合があります。
場合によっては、不完全な一致を伴うパターンが役に立ちます。たとえば、プログラム内のリストに 3 つの要素しかないとわかっている場合は、パラメーター リストで次のようなパターンを使用できます。
let sum [a; b; c;] = a + b + c
不完全な一致を含むパターンの使用は、すばやいプロトタイピングやその他の一時的な使用に最適です。 このようなコードに対しては、コンパイラで警告メッセージが表示されます。 このようなパターンは、入力される可能性のあるすべての値の汎用ケースには対応できないため、コンポーネント API には適していません。
名前付き引数
メソッドの引数は、コンマ区切りの引数リスト内での位置で指定することも、名前を指定し、その後に等号と渡す値を指定して明示的にメソッドに渡すこともできます。 名前で指定する場合は、宣言で使用されている順序と同じ順序にする必要はありません。
名前付き引数を使用すると、コードが読みやすくなり、メソッド パラメーターの順序変更など、API における特定の種類の変更にも対応しやすくなります。
名前付き引数を使用できるのはメソッドのみです。let でバインドされた関数、関数の値、およびラムダ式では使用できません。
名前付き引数を使用するコード例を次に示します。
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# のオプション型として解釈されるので、Some と None を指定した match 式を使用して、オプション型を照会する通常の方法で照会できます。 省略可能なパラメーターを使用できるのはメンバーのみであり、let 束縛を使用して作成された関数では使用できません。
また、省略可能な引数の既定値を設定する defaultArg 関数を使用することもできます。 defaultArg 関数は、省略可能なパラメーターを最初の引数として受け取り、既定値を 2 番目の引数として受け取ります。
省略可能なパラメーターの使用例を次に示します。
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# では、この目的でタプルを返すことも、変更可能な参照セルをパラメーターとして使用することもできます。 byref パラメーターは、主に、.NET ライブラリとの相互運用性のために用意されています。
byref キーワードの使用例を次に示します。 参照セルをパラメーターとして使用する場合は、次のコードの Increment への最初の呼び出しに示されている ref 演算子を追加するだけでなく、参照セルを名前付きの値として作成し、その参照セルをパラメーターとして使用する必要があります。 参照セルを作成すると基の値のコピーが作成されるため、最初の呼び出しでは、単に、一時的な値がインクリメントされます。
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
タプルを戻り値として使用して、out パラメーターを .NET ライブラリ メソッドに格納できます。 また、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