サーチ…
単純なアクティブパターン
アクティブパターンは、データが入る可能性のある名前付きカテゴリを指定して、 match
ステートメントでそれらのカテゴリを使用できる特別なタイプのパターンマッチングです。
数値を正、負またはゼロとして分類するアクティブパターンを定義するには:
let (|Positive|Negative|Zero|) num =
if num > 0 then Positive
elif num < 0 then Negative
else Zero
これは、パターンマッチング式で使用できます。
let Sign value =
match value with
| Positive -> printf "%d is positive" value
| Negative -> printf "%d is negative" value
| Zero -> printf "The value is zero"
Sign -19 // -19 is negative
Sign 2 // 2 is positive
Sign 0 // The value is zero
パラメータを含むアクティブなパターン
アクティブなパターンは単純な関数です。
関数のように、追加のパラメータを定義することができます:
let (|HasExtension|_|) expected (uri : string) =
let result = uri.EndsWith (expected, StringComparison.CurrentCultureIgnoreCase)
match result with
| true -> Some true
| _ -> None
これは、この方法で一致するパターンで使用できます。
let isXMLFile uri =
match uri with
| HasExtension ".xml" _ -> true
| _ -> false
アクティブなパターンを使用して、関数の引数を検証および変換できます
F#
での興味深い、むしろ未知のActive Patternsの使い方は、関数の引数の検証と変換に使用できるということです。
引数の検証を行う古典的な方法を考えてみましょう。
// val f : string option -> string option -> string
let f v u =
let v = defaultArg v "Hello"
let u = defaultArg u "There"
v + " " + u
// val g : 'T -> 'T (requires 'T null)
let g v =
match v with
| null -> raise (System.NullReferenceException ())
| _ -> v.ToString ()
通常、メソッドにコードを追加して、引数が正しいことを確認します。 F#
アクティブなパターンを使用すると、これを一般化して引数の宣言でインテントを宣言できます。
次のコードは上記のコードと同じです:
let inline (|DefaultArg|) dv ov = defaultArg ov dv
let inline (|NotNull|) v =
match v with
| null -> raise (System.NullReferenceException ())
| _ -> v
// val f : string option -> string option -> string
let f (DefaultArg "Hello" v) (DefaultArg "There" u) = v + " " + u
// val g : 'T -> string (requires 'T null)
let g (NotNull v) = v.ToString ()
関数f
とg
のユーザーにとって、2つの異なるバージョン間に違いはありません。
printfn "%A" <| f (Some "Test") None // Prints "Test There"
printfn "%A" <| g "Test" // Prints "Test"
printfn "%A" <| g null // Will throw
Active Patternsがパフォーマンスオーバーヘッドを追加するかどうかが懸念されます。その場合、 ILSpy
を使ってf
とg
を逆コンパイルしてみましょう。
public static string f(FSharpOption<string> _arg2, FSharpOption<string> _arg1)
{
return Operators.DefaultArg<string>(_arg2, "Hello") + " " + Operators.DefaultArg<string>(_arg1, "There");
}
public static string g<a>(a _arg1) where a : class
{
if (_arg1 != null)
{
a a = _arg1;
return a.ToString();
}
throw new NullReferenceException();
}
inline
のおかげで、Active Patternsは従来の引数検証の方法と比べて余分なオーバーヘッドを追加しません。
.NET APIラッパーとしてのアクティブなパターン
Active Patternsを使用すると、.NET APIをより自然に呼び出すことができます。特に、出力パラメータを使用して関数の戻り値以上の結果を返すようなものを使用することができます。
たとえば、通常、 System.Int32.TryParse
メソッドを次のように呼び出します。
let couldParse, parsedInt = System.Int32.TryParse("1")
if couldParse then printfn "Successfully parsed int: %i" parsedInt
else printfn "Could not parse int"
パターンマッチングを使用してこれを少し改善することができます:
match System.Int32.TryParse("1") with
| (true, parsedInt) -> printfn "Successfully parsed int: %i" parsedInt
| (false, _) -> printfn "Could not parse int"
ただし、 System.Int32.TryParse
関数をラップする次のアクティブパターンを定義することもできます。
let (|Int|_|) str =
match System.Int32.TryParse(str) with
| (true, parsedInt) -> Some parsedInt
| _ -> None
これで、次のことが可能になりました。
match "1" with
| Int parsedInt -> printfn "Successfully parsed int: %i" parsedInt
| _ -> printfn "Could not parse int"
Active Patternsでラップされるもう1つの良い候補は、正規表現APIです。
let (|MatchRegex|_|) pattern input =
let m = System.Text.RegularExpressions.Regex.Match(input, pattern)
if m.Success then Some m.Groups.[1].Value
else None
match "bad" with
| MatchRegex "(good|great)" mood ->
printfn "Wow, it's a %s day!" mood
| MatchRegex "(bad|terrible)" mood ->
printfn "Unfortunately, it's a %s day." mood
| _ ->
printfn "Just a normal day"
完全および部分アクティブパターン
アクティブパターンには、使用方法が多少異なる「完全」と「部分」の2種類があります。
完全なアクティブパターンは、「数字が奇数か偶数か」などのすべての結果を列挙できるときに使用できます。
let (|Odd|Even|) number =
if number % 2 = 0
then Even
else Odd
アクティブパターン定義には、可能性のあるケースとそれ以外のものの両方がリストされており、本文はリストされているケースの1つを返します。一致式で使用すると、これは完全一致です。
let n = 13
match n with
| Odd -> printf "%i is odd" n
| Even -> printf "%i is even" n
これは、入力スペースを完全にカバーする既知のカテゴリに分割したい場合に便利です。
一方、部分アクティブパターンは、 option
返すことによって、可能な結果を明示的に無視しoption
。彼らの定義は、比類のない場合に_
特別な場合を使用します。
let (|Integer|_|) str =
match Int32.TryParse(str) with
| (true, i) -> Some i
| _ -> None
このようにして、解析関数によっては処理できないケースもあります。
let s = "13"
match s with
| Integer i -> "%i was successfully parsed!" i
| _ -> "%s is not an int" s
パーシャルアクティブパターンは、入力が他のオプションを無視して入力スペースの特定のカテゴリに入るかどうかにかかわらず、テストの形式として使用できます。