アクティブ パターンActive Patterns

アクティブパターンを使用すると、入力データを分割する名前付きパーティションを定義できます。これにより、判別共用体の場合と同様に、パターン一致式でこれらの名前を使用できるようになります。Active patterns enable you to define named partitions that subdivide input data, so that you can use these names in a pattern matching expression just as you would for a discriminated union. アクティブ パターンを使用すると、パーティションごとにカスタマイズした方法でデータを分解できます。You can use active patterns to decompose data in a customized manner for each partition.


// Active pattern of one choice.
let (|identifier|) [arguments] valueToMatch = expression

// Active Pattern with multiple choices.
// Uses a FSharp.Core.Choice<_,...,_> based on the number of case names. In F#, the limitation n <= 7 applies.
let (|identifer1|identifier2|...|) valueToMatch = expression

// Partial active pattern definition.
// Uses a FSharp.Core.option<_> to represent if the type is satisfied at the call site.
let (|identifier|_|) [arguments ] valueToMatch = expression


前の構文では、識別子は引数によって表される入力データのパーティションの名前、つまり、引数のすべての値のセットのサブセットの名前です。In the previous syntax, the identifiers are names for partitions of the input data that is represented by arguments, or, in other words, names for subsets of the set of all values of the arguments. アクティブなパターン定義には、最大7つのパーティションを指定できます。There can be up to seven partitions in an active pattern definition. では、データを分解するフォームを記述します。The expression describes the form into which to decompose the data. アクティブパターン定義を使用すると、引数として指定された値を持つ名前付きパーティションを決定するための規則を定義できます。You can use an active pattern definition to define the rules for determining which of the named partitions the values given as arguments belong to. (| および |) 記号はバナナクリップと呼ばれ、この型の let バインドによって作成される関数は、アクティブレコグナイザーと呼ばれます。The (| and |) symbols are referred to as banana clips and the function created by this type of let binding is called an active recognizer.

例として、引数を持つ次のアクティブパターンについて考えてみます。As an example, consider the following active pattern with an argument.

let (|Even|Odd|) input = if input % 2 = 0 then Even else Odd

次の例に示すように、パターンマッチング式でアクティブパターンを使用できます。You can use the active pattern in a pattern matching expression, as in the following example.

let TestNumber input =
   match input with
   | Even -> printfn "%d is even" input
   | Odd -> printfn "%d is odd" input

TestNumber 7
TestNumber 11
TestNumber 32

このプログラムの出力は次のとおりです。The output of this program is as follows:

7 is odd
11 is odd
32 is even

アクティブパターンのもう1つの用途は、データ型を複数の方法で分解することです。たとえば、同じ基になるデータにさまざまな表現が含まれている場合などです。Another use of active patterns is to decompose data types in multiple ways, such as when the same underlying data has various possible representations. たとえば、オブジェクトを Color RGB 表現または HSB 表現に分解することができます。For example, a Color object could be decomposed into an RGB representation or an HSB representation.

open System.Drawing

let (|RGB|) (col : System.Drawing.Color) =
     ( col.R, col.G, col.B )

let (|HSB|) (col : System.Drawing.Color) =
   ( col.GetHue(), col.GetSaturation(), col.GetBrightness() )

let printRGB (col: System.Drawing.Color) =
   match col with
   | RGB(r, g, b) -> printfn " Red: %d Green: %d Blue: %d" r g b

let printHSB (col: System.Drawing.Color) =
   match col with
   | HSB(h, s, b) -> printfn " Hue: %f Saturation: %f Brightness: %f" h s b

let printAll col colorString =
  printfn "%s" colorString
  printRGB col
  printHSB col

printAll Color.Red "Red"
printAll Color.Black "Black"
printAll Color.White "White"
printAll Color.Gray "Gray"
printAll Color.BlanchedAlmond "BlanchedAlmond"

上記のプログラムの出力は次のようになります。The output of the above program is as follows:

 Red: 255 Green: 0 Blue: 0
 Hue: 360.000000 Saturation: 1.000000 Brightness: 0.500000
 Red: 0 Green: 0 Blue: 0
 Hue: 0.000000 Saturation: 0.000000 Brightness: 0.000000
 Red: 255 Green: 255 Blue: 255
 Hue: 0.000000 Saturation: 0.000000 Brightness: 1.000000
 Red: 128 Green: 128 Blue: 128
 Hue: 0.000000 Saturation: 0.000000 Brightness: 0.501961
 Red: 255 Green: 235 Blue: 205
 Hue: 36.000000 Saturation: 1.000000 Brightness: 0.901961

これらの2つの方法でアクティブパターンを使用することにより、データを適切な形式に分割および分解し、計算に最も便利な形式の適切なデータに対して適切な計算を実行できます。In combination, these two ways of using active patterns enable you to partition and decompose data into just the appropriate form and perform the appropriate computations on the appropriate data in the form most convenient for the computation.

結果として得られるパターン一致式により、非常に読みやすい方法でデータを書き込むことができるため、複雑な分岐やデータ分析コードを大幅に簡素化できます。The resulting pattern matching expressions enable data to be written in a convenient way that is very readable, greatly simplifying potentially complex branching and data analysis code.

部分的なアクティブパターンPartial Active Patterns

場合によっては、入力領域の一部だけをパーティション分割する必要があります。Sometimes, you need to partition only part of the input space. その場合は、それぞれの入力に一致し、他の入力と一致しない部分パターンのセットを記述します。In that case, you write a set of partial patterns each of which match some inputs but fail to match other inputs. 常に値を生成しないアクティブパターンは、部分的なアクティブパターンと呼ばれます。これらの値は、オプションの型である戻り値を持ちます。Active patterns that do not always produce a value are called partial active patterns; they have a return value that is an option type. 部分的なアクティブパターンを定義するには、 _ バナナクリップ内のパターンの一覧の末尾にワイルドカード文字 () を使用します。To define a partial active pattern, you use a wildcard character (_) at the end of the list of patterns inside the banana clips. 次のコードは、部分的なアクティブパターンの使用方法を示しています。The following code illustrates the use of a partial active pattern.

let (|Integer|_|) (str: string) =
   let mutable intvalue = 0
   if System.Int32.TryParse(str, &intvalue) then Some(intvalue)
   else None

let (|Float|_|) (str: string) =
   let mutable floatvalue = 0.0
   if System.Double.TryParse(str, &floatvalue) then Some(floatvalue)
   else None

let parseNumeric str =
   match str with
     | Integer i -> printfn "%d : Integer" i
     | Float f -> printfn "%f : Floating point" f
     | _ -> printfn "%s : Not matched." str

parseNumeric "1.1"
parseNumeric "0"
parseNumeric "0.0"
parseNumeric "10"
parseNumeric "Something else"

前の例の出力は次のようになります。The output of the previous example is as follows:

1.100000 : Floating point
0 : Integer
0.000000 : Floating point
10 : Integer
Something else : Not matched.

部分的なアクティブパターンを使用する場合、個別の選択肢を相互に不整合にしたり、相互に排他的に指定したりすることはできますが、そうする必要はありません。When using partial active patterns, sometimes the individual choices can be disjoint or mutually exclusive, but they need not be. 次の例では、パターンの Square と pattern キューブは、2つの数値 (64 など) の両方を持つため、不整合にはなりません。In the following example, the pattern Square and the pattern Cube are not disjoint, because some numbers are both squares and cubes, such as 64. 次のプログラムでは、パターンとパターンを使用して、正方形とキューブのパターンを結合しています。The following program uses the AND pattern to combine the Square and Cube patterns. 四角形とキューブの両方であり、キューブのみであるすべての整数が1000まで出力されます。It prints out all integers up to 1000 that are both squares and cubes, as well as those which are only cubes.

let err = 1.e-10

let isNearlyIntegral (x:float) = abs (x - round(x)) < err

let (|Square|_|) (x : int) =
  if isNearlyIntegral (sqrt (float x)) then Some(x)
  else None

let (|Cube|_|) (x : int) =
  if isNearlyIntegral ((float x) ** ( 1.0 / 3.0)) then Some(x)
  else None

let findSquareCubes x =
   match x with
       | Cube x & Square _ -> printfn "%d is a cube and a square" x
       | Cube x -> printfn "%d is a cube" x
       | _ -> ()

[ 1 .. 1000 ] |> List.iter (fun elem -> findSquareCubes elem)

出力は次のようになります。The output is as follows:

1 is a cube and a square
8 is a cube
27 is a cube
64 is a cube and a square
125 is a cube
216 is a cube
343 is a cube
512 is a cube
729 is a cube and a square
1000 is a cube

パラメーター化されたアクティブパターンParameterized Active Patterns

アクティブパターンは、一致する項目に対して少なくとも1つの引数を受け取りますが、追加の引数を受け取る可能性があります。この場合、パラメーター化されたアクティブパターンという名前が適用されます。Active patterns always take at least one argument for the item being matched, but they may take additional arguments as well, in which case the name parameterized active pattern applies. 追加の引数を使用すると、一般的なパターンを特殊化できます。Additional arguments allow a general pattern to be specialized. たとえば、正規表現を使用して文字列を解析するアクティブパターンでは、次のコードに示すように、正規表現を追加のパラメーターとして使用することがよくあります。これは、 Integer 前のコード例で定義された部分的なアクティブパターンも使用します。For example, active patterns that use regular expressions to parse strings often include the regular expression as an extra parameter, as in the following code, which also uses the partial active pattern Integer defined in the previous code example. この例では、さまざまな日付形式の正規表現を使用する文字列が、general ParseRegex アクティブパターンをカスタマイズするために指定されています。In this example, strings that use regular expressions for various date formats are given to customize the general ParseRegex active pattern. 整数アクティブパターンは、一致した文字列を DateTime コンストラクターに渡すことができる整数に変換するために使用されます。The Integer active pattern is used to convert the matched strings into integers that can be passed to the DateTime constructor.

open System.Text.RegularExpressions

// ParseRegex parses a regular expression and returns a list of the strings that match each group in
// the regular expression.
// List.tail is called to eliminate the first element in the list, which is the full matched expression,
// since only the matches for each group are wanted.
let (|ParseRegex|_|) regex str =
   let m = Regex(regex).Match(str)
   if m.Success
   then Some (List.tail [ for x in m.Groups -> x.Value ])
   else None

// Three different date formats are demonstrated here. The first matches two-
// digit dates and the second matches full dates. This code assumes that if a two-digit
// date is provided, it is an abbreviation, not a year in the first century.
let parseDate str =
   match str with
     | ParseRegex "(\d{1,2})/(\d{1,2})/(\d{1,2})$" [Integer m; Integer d; Integer y]
          -> new System.DateTime(y + 2000, m, d)
     | ParseRegex "(\d{1,2})/(\d{1,2})/(\d{3,4})" [Integer m; Integer d; Integer y]
          -> new System.DateTime(y, m, d)
     | ParseRegex "(\d{1,4})-(\d{1,2})-(\d{1,2})" [Integer y; Integer m; Integer d]
          -> new System.DateTime(y, m, d)
     | _ -> new System.DateTime()

let dt1 = parseDate "12/22/08"
let dt2 = parseDate "1/1/2009"
let dt3 = parseDate "2008-1-15"
let dt4 = parseDate "1995-12-28"

printfn "%s %s %s %s" (dt1.ToString()) (dt2.ToString()) (dt3.ToString()) (dt4.ToString())

前のコードの出力は次のようになります。The output of the previous code is as follows:

12/22/2008 12:00:00 AM 1/1/2009 12:00:00 AM 1/15/2008 12:00:00 AM 12/28/1995 12:00:00 AM

アクティブパターンは、パターンマッチング式のみに制限されていません。 let バインドで使用することもできます。Active patterns are not restricted only to pattern matching expressions, you can also use them on let-bindings.

let (|Default|) onNone value =
    match value with
    | None -> onNone
    | Some e -> e

let greet (Default "random citizen" name) =
    printfn "Hello, %s!" name

greet None
greet (Some "George")

前のコードの出力は次のようになります。The output of the previous code is as follows:

Hello, random citizen!
Hello, George!

関連項目See also