Sök…


Enkla aktiva mönster

Aktiva mönster är en speciell typ av mönstermatchning där du kan ange namngivna kategorier som dina data kan falla in i och sedan använda de kategorierna i match .

För att definiera ett aktivt mönster som klassificerar siffror som positiva, negativa eller noll:

let (|Positive|Negative|Zero|) num = 
    if num > 0 then Positive 
    elif num < 0 then Negative
    else Zero

Detta kan sedan användas i ett mönster-matchande uttryck:

let Sign value = 
    match value with
    | Positive -> printf "%d is positive" value
    | Negative -> printf "%d is negative" value
    | Zero -> printf "The value is zero"

Sign -19 // -19 is negative
Sign 2 // 2 is positive
Sign 0 // The value is zero

Aktiva mönster med parametrar

Aktiva mönster är bara enkla funktioner.

Som funktioner kan du definiera ytterligare parametrar:

let (|HasExtension|_|) expected (uri : string) = 
    let result = uri.EndsWith (expected, StringComparison.CurrentCultureIgnoreCase)
    match result with
    | true -> Some true
    | _ -> None

Detta kan användas i ett mönster som matchar detta sätt:

    let isXMLFile uri =
        match uri with
        | HasExtension ".xml" _ -> true
        | _ -> false

Aktiva mönster kan användas för att validera och omvandla funktionsargument

En intressant men ganska okänd användning av aktiva mönster i F# är att de kan användas för att validera och omvandla funktionsargument.

Tänk på det klassiska sättet att göra argumentvalidering:

// val f : string option -> string option -> string
let f v u =
  let v = defaultArg v "Hello"
  let u = defaultArg u "There"
  v + " " + u

// val g : 'T -> 'T (requires 'T null)
let g v =
  match v with
  | null  -> raise (System.NullReferenceException ())
  | _     -> v.ToString ()

Vanligtvis lägger vi till kod i metoden för att verifiera att argument är korrekta. Med hjälp av aktiva mönster i F# vi generalisera detta och förklara avsikten i argumentdeklarationen.

Följande kod motsvarar koden ovan:

let inline (|DefaultArg|) dv ov = defaultArg ov dv

let inline (|NotNull|) v =
  match v with
  | null  -> raise (System.NullReferenceException ())
  | _     -> v

// val f : string option -> string option -> string
let f (DefaultArg "Hello" v) (DefaultArg "There" u) = v + " " + u

// val g : 'T -> string (requires 'T null)
let g (NotNull v) = v.ToString ()

För användaren av funktionen f och g finns det ingen skillnad mellan de två olika versionerna.

printfn "%A" <| f (Some "Test") None  // Prints "Test There"
printfn "%A" <| g "Test"              // Prints "Test"
printfn "%A" <| g null                // Will throw

Ett problem är om Active Patterns lägger till högre prestanda. Låt oss använda ILSpy att dekompilera f och g att se om så är fallet.

public static string f(FSharpOption<string> _arg2, FSharpOption<string> _arg1)
{
  return Operators.DefaultArg<string>(_arg2, "Hello") + " " + Operators.DefaultArg<string>(_arg1, "There");
}

public static string g<a>(a _arg1) where a : class
{
  if (_arg1 != null)
  {
    a a = _arg1;
    return a.ToString();
  }
  throw new NullReferenceException();
}

Tack vare inline lägger de aktiva mönstren inga extra omkostnader jämfört med det klassiska sättet att göra argumentvalidering.

Aktiva mönster som .NET API-omslag

Aktiva mönster kan användas för att göra att vissa .NET API: s känns mer naturliga, särskilt de som använder en utgångsparameter för att returnera mer än bara funktionens returvärde.

Till exempel kan du normalt ringa metoden System.Int32.TryParse enligt följande:

let couldParse, parsedInt = System.Int32.TryParse("1")
if couldParse then printfn "Successfully parsed int: %i" parsedInt
else printfn "Could not parse int"

Du kan förbättra detta lite genom att använda mönstermatchning:

match System.Int32.TryParse("1") with 
| (true, parsedInt) -> printfn "Successfully parsed int: %i" parsedInt
| (false, _) -> printfn "Could not parse int"

Vi kan emellertid också definiera följande aktiva mönster som System.Int32.TryParse funktionen System.Int32.TryParse :

let (|Int|_|) str =
    match System.Int32.TryParse(str) with
    | (true, parsedInt) -> Some parsedInt
    | _ -> None

Nu kan vi göra följande:

match "1" with
| Int parsedInt -> printfn "Successfully parsed int: %i" parsedInt
| _ -> printfn "Could not parse int"

En annan bra kandidat för att vara insvept i ett aktivt mönster är de vanliga uttrycks-API: er:

let (|MatchRegex|_|) pattern input =
    let m = System.Text.RegularExpressions.Regex.Match(input, pattern) 
    if m.Success then Some m.Groups.[1].Value 
    else None  

match "bad" with
| MatchRegex "(good|great)" mood -> 
    printfn "Wow, it's a %s day!" mood
| MatchRegex "(bad|terrible)" mood -> 
    printfn "Unfortunately, it's a %s day." mood
| _ -> 
    printfn "Just a normal day"

Kompletta och delvis aktiva mönster

Det finns två typer av aktiva mönster som något skiljer sig i användning - komplett och delvis.

Kompletta aktiva mönster kan användas när du kan räkna upp alla resultat, som "är ett antal udda eller jämnt?"

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

Lägg märke till att definitionen av aktivt mönster listar både möjliga fall och inget annat, och kroppen returnerar ett av de listade fallen. När du använder det i ett matchuttryck är detta en komplett matchning:

let n = 13
match n with
| Odd -> printf "%i is odd" n
| Even -> printf "%i is even" n

Detta är praktiskt när du vill dela in ingångsutrymmet i kända kategorier som täcker det helt.

Partiella aktiva mönster å andra sidan låter dig uttryckligen ignorera några möjliga resultat genom att returnera ett option . Deras definition använder ett specialfall av _ för det oöverträffade fallet.

let (|Integer|_|) str =
  match Int32.TryParse(str) with
  | (true, i) -> Some i
  | _ -> None

På det här sättet kan vi matcha även om vissa fall inte kan hanteras med vår parsing-funktion.

let s = "13"
match s with
| Integer i -> "%i was successfully parsed!" i
| _ -> "%s is not an int" s

Partiella aktiva mönster kan användas som en form av testning, om ingången faller in i en viss kategori i ingångsutrymmet medan man ignorerar andra alternativ.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow