サーチ…


単純なアクティブパターン

アクティブパターンは、データが入る可能性のある名前付きカテゴリを指定して、 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 ()

関数fgのユーザーにとって、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を使ってfgを逆コンパイルしてみましょう。

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

パーシャルアクティブパターンは、入力が他のオプションを無視して入力スペースの特定のカテゴリに入るかどうかにかかわらず、テストの形式として使用できます。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow