Megosztás a következőn keresztül:


Aktív minták

Az aktív minták lehetővé teszik a bemeneti adatok felosztását végző elnevezett partíciók meghatározását, így ezeket a neveket ugyanúgy használhatja egy mintaegyező kifejezésben, mint egy diszkriminált egyesítés esetében. Az aktív mintákkal testre szabott módon bonthatja le az adatokat az egyes partíciókhoz.

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

Megjegyzések

Az előző szintaxisban az azonosítók a bemeneti adatok partícióinak nevei, amelyeket argumentumok, más szóval az argumentumok összes értékhalmazának alhalmazának nevei jelölnek. Egy aktív mintadefiníció legfeljebb hét partíciót tartalmazhat. A kifejezés azt az űrlapot írja le, amelybe az adatokat fel kell bontani. Egy aktív mintadefinícióval meghatározhatja azokat a szabályokat, amelyek meghatározzák, hogy az elnevezett partíciók közül melyikhez tartoznak az argumentumként megadott értékek. A (| és |) szimbólumokat banánklipeknek nevezzük, és az ilyen típusú let kötés által létrehozott függvényt aktív felismerőnek nevezzük.

Vegyük például az alábbi aktív mintát egy argumentummal.

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

Az aktív mintát egy mintaegyező kifejezésben használhatja, ahogyan az alábbi példában is látható.

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

TestNumber 7
TestNumber 11
TestNumber 32

A program kimenete a következő:

7 is odd
11 is odd
32 is even

Az aktív minták másik használata az adattípusok több módon történő lebontása, például ha ugyanazok a mögöttes adatok különböző lehetséges ábrázolásokkal rendelkeznek. Egy objektum például Color RGB-reprezentációvá vagy HSB-reprezentációvá bontható.

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"

A fenti program kimenete a következő:

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

Az aktív minták ezen két módja együttesen lehetővé teszi az adatok megfelelő formában való particionálását és felbontását, valamint a megfelelő számítások elvégzését a számításhoz legkényelmesebb formában.

Az eredményül kapott mintamegfeleltetési kifejezések lehetővé teszik az adatok kényelmes, nagyon olvasható módon történő megírását, ami jelentősen leegyszerűsíti a potenciálisan összetett elágaztatási és adatelemzési kódot.

Részleges aktív minták

Előfordulhat, hogy a bemeneti területnek csak egy részét kell particionolnia. Ebben az esetben részleges mintákat kell írnia, amelyek mindegyike megfelel bizonyos bemeneteknek, de nem egyezik más bemenetekkel. Azokat az aktív mintákat, amelyek nem mindig hoznak létre értéket, részleges aktív mintáknak nevezzük; a visszatérési értékük egy beállítástípus. Részleges aktív minta definiálásához egy helyettesítő karaktert (_) használ a banánklipeken belüli minták listájának végén. Az alábbi kód egy részleges aktív minta használatát mutatja be.

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"

Az előző példa kimenete a következő:

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

Részleges aktív minták használata esetén előfordulhat, hogy az egyes döntések különállóak vagy kölcsönösen kizárhatók, de nem kell. Az alábbi példában a négyzet és a minta kocka nem különálló, mert egyes számok négyzetek és kockák, például 64. Az alábbi program az AND mintát használja a Négyzet és a Kocka minták kombinálásához. Az 1000-ig összes egész számot kinyomtatja, amelyek négyzetek és kockák, valamint azokat is, amelyek csak kockák.

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)

A kimenet a következő:

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

Paraméteres aktív minták

Az aktív minták mindig legalább egy argumentumot használnak a megfeleltetendő elemhez, de további argumentumokat is igénybe vehetnek, ebben az esetben a név paraméterezett aktív mintát alkalmazza. A további argumentumok lehetővé teszik egy általános minta specializált használatát. A sztringek elemzéséhez reguláris kifejezéseket használó aktív minták például gyakran extra paraméterként használják a reguláris kifejezést, mint az alábbi kódban, amely az előző kód példában definiált részleges aktív mintát Integer is használja. Ebben a példában a különböző dátumformátumokhoz reguláris kifejezéseket használó sztringek az általános ParseRegex aktív minta testreszabására használhatók. Az aktív egész szám mintával a megfeleltethető sztringek egész számokká alakíthatók, amelyek átadhatók a DateTime konstruktornak.

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

Az előző kód kimenete a következő:

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

Az aktív minták nem csak a mintamegfeleltetési kifejezésekre korlátozódnak, hanem let-kötéseken is használhatók.

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

Az előző kód kimenete a következő:

Hello, random citizen!
Hello, George!

Vegye figyelembe azonban, hogy csak az egy esetből álló aktív minták paraméterezhetők.

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

Strukturált ábrázolások részleges aktív mintákhoz

Alapértelmezés szerint a részleges aktív minták egy option értéket adnak vissza, amely egy sikeres egyezéshez tartozó érték lefoglalását Some foglalja magában. Másik lehetőségként egy értékbeállítást is használhat visszatérési értékként az Struct attribútum használatával:

open System

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

Az attribútumot meg kell adni, mert a visszatérési típus ValueOptionegyszerű módosításából nem következik a strukturált visszatérés használata. További információ: RFC FS-1039.

Lásd még