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


Aktív minták

Aktív minták lehetővé teszik a bemeneti adatok felosztását lehetővé tevő elnevezett partíciók meghatározását, így ezeket a neveket ugyanúgy használhatja egy mintaegyező kifejezésben, mint a 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.

Szintaxis

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

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 lehetséges értékének halmazához tartozó alhalmazok nevei. Egy aktív mintadefiníció legfeljebb hét partíciót tartalmazhat. A kifejezés azt az alakot írja le, amelyre az adatokat le 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ánklipek nevezik, és az ilyen típusú let kötés által létrehozott függvényt aktív felismerőnevezik.

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 Color objektum például RGB- vagy HSB-reprezentációra 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; olyan visszatérési értékkel rendelkeznek, amely 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 ez nem szükségszerű. 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áknak mindig van legalább egy argumentumuk a vizsgált elemhez, de további argumentumokat is vehetnek, ebben az esetben az úgynevezett paraméterezett aktív mintát vonatkozik. 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 is használja Integer. 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-bindingek esetén 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

Részleges aktív minták visszatérési típusa

A részleges aktív minták Some ()-t adnak vissza egyezés esetén, különben None-et.

Fontolja meg ezt a mérkőzést:

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

A részleges aktív minta a következő lenne:

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

Az F# 9-től kezdve az ilyen minták is visszaadhatják a bool-t:

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

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

Alapértelmezés szerint, ha egy részleges aktív minta egy option-t ad vissza, ez magában foglalja a Some érték lefoglalását, ha a találat sikeres. Ennek elkerülése érdekében a attribútum használatával értékbeállítást használhatja visszatérési értékként:

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 strukturált visszatérés használata nem következik pusztán a visszatérési típus ValueOption-ra való megváltoztatásából. További információ: RFC FS-1039.

Null aktív minták

Az F# 9-ben nullabilitással kapcsolatos aktív minták lettek hozzáadva.

Az első a | Null | NonNull x |, amely ajánlott módszer a lehetséges null értékek kezelésére. Az alábbi példában a s paraméter az aktív mintahasználattal null értékű:

 let len s =
    match s with
    | Null -> -1
    | NonNull s -> String.length s

Ha inkább NullReferenceExceptionszeretne automatikusan dobni, használja a | NonNullQuick | mintát:

let len (NonNullQuick str) =  // throws if the argument is null
    String.length str

Lásd még: