Padrões de ativos (F#)
Padrões Active permitem que você definir partições nomeadas que subdividir os dados de entrada, para que você possa usar esses nomes em um padrão de expressão de correspondência, assim como faria em uma união discriminada. Você pode usar padrões de ativos para decompor os dados de maneira personalizada para cada partição.
// Complete active pattern definition.
let (|identifer1|identifier2|...|) [ arguments ] = expression
// Partial active pattern definition.
let (|identifier1|identifier2|...|_|) [ arguments ] = expression
Comentários
Na sintaxe anterior, os identificadores são nomes de partições de dados de entrada são representados por arguments, ou, em outras palavras, os nomes de subconjuntos do conjunto de todos os valores dos argumentos. Pode haver até sete partições em uma definição padrão ativo. O expression descreve o formulário no qual será a decompor os dados. Você pode usar uma definição padrão ativo para definir as regras para determinar qual das partições nomeadas valores fornecidos como argumentos pertencem. O (| e |) símbolos são chamados de os clipes de banana e a função criada por esse tipo de associação let é chamada um reconhecedor active.
Por exemplo, considere o seguinte padrão de ativo com um argumento.
let (|Even|Odd|) input = if input % 2 = 0 then Even else Odd
Você pode usar o padrão ativo em um padrão de correspondência da expressão, como no exemplo a seguir.
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 saída deste programa é o seguinte:
7 is odd
11 is odd
32 is even
Outro uso de padrões de ativos é decompor os tipos de dados de várias formas, como, por exemplo, quando os mesmos dados subjacentes tem várias representações possíveis. Por exemplo, um Color objeto poderia ser decomposto em uma representação de RGB ou uma representação 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"
A saída do programa acima é o seguinte:
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
Juntas, essas duas maneiras de usar os padrões de active permitem a partição e decompõem os dados no formulário apropriado em executam as computações apropriadas nos dados apropriados na forma mais conveniente para a computação.
As expressões de correspondência de padrão resultante permitem que os dados sejam gravados em uma forma conveniente de ramificação potencialmente complexa muito legível, simplificando bastante e código de análise de dados.
Padrões de Active parciais
Às vezes, você precisa apenas uma parte do espaço de entrada de partição. Nesse caso, você escreve um conjunto parcial padrões que correspondam a algumas entradas, mas não corresponderem a outras entradas. Padrões de ativos que não produzem um valor sempre são chamados de parciais padrões active; eles têm um valor de retorno é um tipo de opção. Para definir um padrão de active parcial, você pode usar um caractere curinga (_) no final da lista de padrões dentro dos clipes de banana. O código a seguir ilustra o uso de um padrão de active parcial.
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"
A saída do exemplo anterior é o seguinte:
1.100000 : Floating point
0 : Integer
0.000000 : Floating point
10 : Integer
Something else : Not matched.
Ao usar padrões de active parciais, às vezes, as opções individuais podem ser disjunção ou mutuamente exclusivos, mas eles não precisam ser. No exemplo a seguir, o quadrado do padrão e o cubo de padrão não são disjunção, porque alguns números são quadrados e cubos, como, por exemplo, 64. O programa a seguir imprime todos os inteiros até 1000000 quadrados e cubos.
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)
A saída é o seguinte:
1
64
729
4096
15625
46656
117649
262144
531441
1000000
Padrões de ativos com parâmetros
Padrões Active sempre têm pelo menos um argumento para o item que está sendo correspondido, mas podem se os argumentos adicionais, caso em que o nome parametrizado padrão ativo se aplica. Argumentos adicionais permitem que um padrão geral ser especializados. Por exemplo, o active padrões que usam expressões regulares para analisar cadeias de caracteres geralmente incluem a expressão regular como um parâmetro extra, como no código a seguir, que também usa o padrão de active parcial Integer definido, o exemplo de código anterior. Neste exemplo, seqüências de caracteres, dando a expressões regulares para vários formatos de data são fornecidas para personalizar o padrão de ativo de ParseRegex geral. O padrão de ativo de inteiro é usado para converter as cadeias correspondidas em inteiros podem ser passados para o construtor 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())
A saída do código anterior é o seguinte:
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