共用方式為


主動模式

作用中模式 可讓您定義具名分割區,以細分輸入數據,讓您可以在模式比對表達式中使用這些名稱,就像對差異聯集一樣。 您可以使用作用中的模式,以自定義方式分解每個分割區的數據。

語法

// 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 (|identifier1|identifier2|...|) valueToMatch = expression

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

備註

在上一個語法中,標識符是用來表示由 參數所代表的輸入數據分區的名稱,換句話說,是所有參數值集合之子集的名稱。 使用中模式定義中最多可以有七個分割區。 表示式 描述要分解數據的形式。 您可以使用作用中的模式定義來定義規則,以判斷指定為自變數的值所屬的具名分割區。 (| 和 |)符號被稱為 香蕉夾,而由這種類型的 let 綁定所建立的函式被稱為 主動識別器

例如,考慮以下包含一個參數的活動模式。

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

您可以在模式比對表達式中使用活動模式,如下列範例所示。

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

TestNumber 7
TestNumber 11
TestNumber 32

此程式輸出如下所示:

7 is odd
11 is odd
32 is even

使用中模式的另一種用法是以多種方式分解數據類型,例如,當相同的基礎數據具有各種可能表示法時。 例如,Color 物件可能會分解成 RGB 表示法或 HSB 表示法。

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"

上述程式輸出如下所示:

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

結合這兩種使用主動模式的方式,可讓您將數據分割並分解成適當的形式,並在最方便計算的形式中對適當數據執行適當的計算。

產生的模式比對表達式可讓數據以非常易讀的方式撰寫,大幅簡化可能複雜的分支和數據分析程序代碼。

部分主動模式

有時候,您只需要分割輸入空間的一部分。 在此情況下,您會撰寫一組部分模式,其中每個模式都符合某些輸入,但無法比對其他輸入。 不一定會產生值的主動模式稱為 部分主動模式,其傳回值是選擇類型。 若要定義部分使用中的模式,您可以在香蕉剪輯內的圖樣清單結尾使用通配符 (_)。 下列程式代碼說明如何使用部分主動模式。

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"

上一個範例的輸出如下所示:

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

使用部分主動模式時,有時候個別的選擇可能會相互分離或互斥,但不一定必須如此。 在下列範例中,平方和立方的數字並不是互不相交的,因為有些數字同時是平方和立方,例如 64。 下列程式會使用 AND 模式來結合方形和 Cube 模式。 它會列印出小於或等於 1000 的所有既是平方數又是立方數的整數,以及只有立方數的整數。

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)

輸出如下所示:

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

參數化的主動模式

使用中模式一律會針對相符的專案至少採用一個自變數,但也可能採用其他自變數,在此情況下,參數化使用中模式的名稱 套用。 其他自變數可讓一般模式特製化。 例如,使用正則表示式來剖析字串的主動模式,通常會將正則表示式作為額外的參數,如下列程式代碼所示。而這段程式碼也會使用之前程式碼範例中定義的部分主動模式 Integer。 在此範例中,利用正則表達式的字串來處理不同的日期格式,以自定義一般的 ParseRegex 活動模式。 主動模式用於將相符的字串轉換成可傳遞至 DateTime 建構函式的整數。

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())

上一個程式代碼的輸出如下所示:

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-binding 上使用它們。

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")

上一個程式代碼的輸出如下所示:

Hello, random citizen!
Hello, George!

不過請注意,只有單一案例的作用模式可以參數化。

// A single-case partial active pattern can be parameterized
let (| Foo|_|) s x = if x = s then Some Foo else None
// A multi-case active patterns cannot be parameterized
// let (| Even|Odd|Special |) (s: int) (x: int) = if x = s then Special elif x % 2 = 0 then Even else Odd

部分活躍模式的傳回型別

部分活躍模式會傳回 Some (),以指示相符,否則傳回 None

請考慮這個匹配项:

match key with
| CaseInsensitive "foo" -> ...
| CaseInsensitive "bar" -> ...

部分活躍模式為:

let (|CaseInsensitive|_|) (pattern: string) (value: string) =
    if String.Equals(value, pattern, StringComparison.OrdinalIgnoreCase) then
        Some ()
    else
        None

從 F# 9 開始,這類模式也可以傳回 bool

let (|CaseInsensitive|_|) (pattern: string) (value: string) =
    String.Equals(value, pattern, StringComparison.OrdinalIgnoreCase)

部分的主動模式之結構表示法

預設情況下,如果部分啟用的模式傳回 option,則這會涉及為成功匹配的 Some 值進行分配。 若要避免此問題,您可以使用 值選項 作為傳回值,透過使用 Struct 屬性:

open System

[<return: Struct>]
let (|Int|_|) str =
   match Int32.TryParse(str) with
   | (true, n) -> ValueSome n
   | _ -> ValueNone

必須指定 屬性,因為不會從將傳回型別變更為 ValueOption來推斷結構傳回的用法。 如需詳細資訊,請參閱 RFC FS-1039

另請參閱