Aktive Muster (F#)

Mit aktiven Mustern können Sie benannte Partitionen für die Unterteilung von Eingabedaten definieren, sodass Sie diese Namen in einem Mustervergleichsausdruck auf die gleiche Weise wie in einer Unterscheidungs-Union verwenden können. Mit aktiven Mustern können Sie Daten für jede Partition benutzerdefiniert zerlegen.

// Complete active pattern definition.
let (|identifer1|identifier2|...|) [ arguments ] = expression
// Partial active pattern definition.
let (|identifier1|identifier2|...|_|) [ arguments ] = expression

Hinweise

In der vorherigen Syntax sind die Bezeichner Namen für Partitionen der Eingabedaten, die durch arguments dargestellt werden, d. h. Namen für Teilmengen des Satzes aller Werte der Argumente. Es können bis zu sieben Partitionen in der Definition eines aktiven Musters sein. Die expression beschreibt die Form, in der die Daten zerlegt werden sollen. Mit der Definition eines aktiven Musters können Sie die Regeln festlegen, nach denen bestimmt wird, zu welchen benannten Partitionen die als Argumente angegebenen Werte gehören. Die Symbole (| und |) werden als Bananenklammern bezeichnet, und die von diesem Typ der let-Bindung erstellte Funktion wird als aktive Erkennung bezeichnet.

Betrachten Sie als Beispiel das folgende aktive Muster mit einem Argument.

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

Sie können das aktive Muster in einem Mustervergleichsausdruck verwenden, wie im folgenden Beispiel.

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

TestNumber 7
TestNumber 11
TestNumber 32

Dieses Programm generiert die folgende Ausgabe:

7 is odd
11 is odd
32 is even

Aktive Muster können auch verwendet werden, um Datentypen auf verschiedene Weise zu zerlegen, z. B. wenn der gleiche zugrunde liegende Datentyp über mehrere mögliche Darstellungen verfügt. Beispielsweise kann ein Color-Objekt in eine RGB-Darstellung oder eine HSB-Darstellung zerlegt werden.

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"

Das oben genannte Programm generiert die folgende Ausgabe:

Red
 R: 255 G: 0 B: 0
 H: 0.000000 S: 1.000000 B: 0.500000
Black
 R: 0 G: 0 B: 0
 H: 0.000000 S: 0.000000 B: 0.000000
White
 R: 255 G: 255 B: 255
 H: 0.000000 S: 0.000000 B: 1.000000
Gray
 R: 128 G: 128 B: 128
 H: 0.000000 S: 0.000000 B: 0.501961
BlanchedAlmond
 R: 255 G: 235 B: 205
 H: 36.000000 S: 1.000000 B: 0.901961

Diese beiden Verwendungsarten aktiver Muster gemeinsam ermöglichen es Ihnen, Daten in die geeignete Form zu untergliedern und zu zerlegen und die entsprechenden Berechnungen für die jeweiligen Daten in der zweckmäßigsten Form für die Berechnung auszuführen.

Die resultierenden Mustervergleichsausdrücke ermöglichen es, Daten auf unkomplizierte Weise zu schreiben, sodass die Lesbarkeit erhöht und potenziell komplexer Code für Verzweigungen und Datenanalyse immens vereinfacht wird.

Partielle aktive Muster

Manchmal müssen Sie nur einen Teil der Eingaben partitionieren. In diesem Fall schreiben Sie einen Satz von partiellen Mustern, von denen jedes mit einigen Eingaben übereinstimmt und mit anderen Eingaben nicht übereinstimmt. Aktive Muster, die nicht immer einen Wert liefern, werden als partielle aktive Muster bezeichnet. Ihr Rückgabewert ist ein Optionstyp. Sie definieren ein partielles aktives Muster mit einem Platzhalterzeichen (_) am Ende der Liste von Mustern innerhalb der Bananenklammern. Im folgenden Code wird die Verwendung eines partiellen aktiven Musters veranschaulicht.

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"

Die Ausgabe des vorherigen Beispiels lautet wie folgt:

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

Wenn sie partielle aktive Muster verwenden, können die einzelnen Optionen manchmal disjunkt sein bzw. sich gegenseitig ausschließen. Dies ist jedoch nicht zwangsläufig der Fall. Im folgenden Beispiel sind die Square- und Cube-Muster nicht disjunkt, da einige Zahlen sowohl Quadrate als auch Cubes, z. B. 64, sind. Das folgende Programm gibt alle ganzen Zahlen bis 1 000 000 aus, die sowohl Quadrate als auch Cubes sind.

let err = 1.e-10

let floatequal x y =
   abs (x - y) < err

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

let (|Cube|_|) (x : int) =
  if floatequal ((float x) ** ( 1.0 / 3.0)) (round ((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 =
   if (match x with
       | Cube x -> true
       | _ -> false
       &&
       match x with
       | Square x -> true
       | _ -> false
      )
   then printf "%d \n" x

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

Die Ausgabe lautet wie folgt:

1
64
729
4096
15625
46656
117649
262144
531441
1000000

Parametrisierte aktive Muster

Aktive Muster akzeptieren immer mindestens ein Argument für das zu vergleichende Element, jedoch akzeptieren sie möglicherweise auch zusätzliche Argumente. In diesem Fall handelt es sich um ein parametrisiertes aktives Muster. Mit zusätzlichen Argumenten kann ein allgemeines Muster in ein spezielles Muster geändert werden. Beispielsweise enthalten aktive Muster, die Zeichenfolgen mithilfe regulärer Ausdrücke analysieren, oft den regulären Ausdruck als zusätzlichen Parameter, wie im folgenden Code, in dem außerdem das partielle aktive Muster Integer verwendet wird, das im vorherigen Codebeispiel definiert wurde. In diesem Beispiel werden Zeichenfolgen angegeben, die reguläre Ausdrücke für verschiedene Datumsformate bilden, um das allgemeine aktive Muster ParseRegex anzupassen. Mit dem aktiven Muster Integer werden die entsprechenden Zeichenfolgen in ganze Zahlen konvertiert, die an den DateTime-Konstruktor übergeben werden können.

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

Die Ausgabe des obigen Codes lautet wie folgt:

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

Siehe auch

Referenz

Vergleichsausdrücke (F#)

Weitere Ressourcen

F#-Sprachreferenz