現用模式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.

語法Syntax

// 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

備註Remarks

在先前的語法中,識別碼是以引數表示之輸入資料的資料分割名稱,換句話說,是引數的所有值集合的子集名稱。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. 現用模式定義中最多可以有七個數據分割。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

使用中模式的另一種用法是以多種方式分解資料類型,例如,當相同的基礎資料有各種可能的標記法時。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
 Red: 255 Green: 0 Blue: 0
 Hue: 360.000000 Saturation: 1.000000 Brightness: 0.500000
Black
 Red: 0 Green: 0 Blue: 0
 Hue: 0.000000 Saturation: 0.000000 Brightness: 0.000000
White
 Red: 255 Green: 255 Blue: 255
 Hue: 0.000000 Saturation: 0.000000 Brightness: 1.000000
Gray
 Red: 128 Green: 128 Blue: 128
 Hue: 0.000000 Saturation: 0.000000 Brightness: 0.501961
BlanchedAlmond
 Red: 255 Green: 235 Blue: 205
 Hue: 36.000000 Saturation: 1.000000 Brightness: 0.901961

結合使用現用模式時,這兩種方式可讓您將資料分割和分解成適當的形式,並針對計算最方便的形式,在適當的資料上執行適當的計算。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. 在下列範例中,模式正方形和模式 Cube 不是連續的,因為有些數位同時為正方形和 cube,例如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. 下列程式會使用和模式來結合正方形和 Cube 模式。The following program uses the AND pattern to combine the Square and Cube patterns. 它會列印出最多1000的所有整數,其中同時為正方形和 cube,以及僅為 cube。It print 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 examineNumber x =
   match x with
      | Cube x -> printfn "%d is a cube" x
      | _ -> ()
   match x with
      | Square x -> printfn "%d is a square" x
      | _ -> ()

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

現用模式一律會針對要比對的專案使用至少一個引數,但它們也可能會採用其他引數,在此情況下,會套用參數化現用模式的名稱。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. 在此範例中,會提供使用適用于各種日期格式之正則運算式的字串,以自訂一般 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