Correspondência padrão (F#)
Os padrões são regras para transformar dados de entrada. Eles são usados na linguagem F# para comparar dados com uma ou mais estruturas lógicas, para decompor dados em partes constituintes ou para extrair informações de dados de várias maneiras.
Comentários
Os padrões são usados em muitas compilações de linguagem, como a expressão de match. Eles são usados quando você está processando argumentos para funções em associações let, expressões lambda e nos manipuladores de exceção associados à expressão try...with. Para obter mais informações, consulte Expressões match (F#), Associações let (F#), Expressões lambda: a palavra-chave fun (F#) e Exceções: a expressão try...with (F#).
Por exemplo, na expressão match, pattern é o que segue o símbolo de pipe.
fazer a correspondência de expression com
| pattern [ when condition ] -> result-expression
...
Cada padrão atua como uma regra para a transformação de entrada de alguma maneira. Na expressão match, cada padrão é examinado em turnos para verificar se os dados de entrada são compatíveis com o padrão. Se uma correspondência for encontrada, a expressão de resultado será executada. Se uma correspondência não for encontrada, a próxima regra de padrão será testada. O opcional quando a parte condition é explicada em Expressões match (F#).
Os padrões suportados são mostrados na tabela a seguir. No tempo de execução, a entrada é testada contra cada um dos seguintes padrões na ordem listada na tabela, e os padrões são aplicados recursivamente, do primeiro ao último conforme aparecerem no seu código, e da esquerda para a direita para os padrões em cada linha.
Nome |
Descrição |
Exemplo |
---|---|---|
Padrão constante |
Qualquer numérico, caractere, ou literal de cadeia de caracteres, uma constante de enumeração, ou um identificador literal definido |
1.0, "test", 30, Color.Red |
Padrão do identificador |
Um valor de caso de uma união discriminada, um rótulo de exceção, ou um caso de padrão ativo |
Some(x) Failure(msg) |
Padrão variável |
identifier |
a |
as padrão |
pattern como identifier |
(a, b) as tuple1 |
Padrão OR |
pattern1 | pattern2 |
([h] | [h; _]) |
Padrão de AND |
pattern1 & pattern2 |
(a, b) & (_, "test") |
Padrão constante |
identifier :: list-identifier |
h :: t |
Padrão de lista |
[ pattern_1; ... ; pattern_n ] |
[ a; b; c ] |
Padrão de matriz |
[| pattern_1; ..; pattern_n ] |
[| a; b; c |] |
Padrão posto entre parênteses |
( pattern ) |
( a ) |
Padrão da tupla |
( pattern_1, ... , pattern_n ) |
( a, b ) |
Padrão de gravação |
{ identifier1 = pattern_1; ... ; identifier_n = pattern_n } |
{ Name = name; } |
Padrão de curinga |
_ |
_ |
Padrão juntamente com a anotação de tipo |
pattern : type |
a : int |
Padrão de teste de tipo |
:? type [ como identifier ] |
:? System.DateTime as dt |
Padrão zero |
null |
null |
Padrões constante
Os padrões constantes são literais numéricas, de caractere e de cadeia de caracteres, constantes de enumeração (com o nome do tipo de enumeração incluído). Uma expressão de match que possui apenas constantes padrão pode ser comparada a uma instrução case em outras linguagens. A entrada é comparada com o valor literal e o padrão corresponde se os valores são iguais. O tipo do literal deve ser compatível com o tipo da entrada.
O exemplo a seguir demonstra o uso de padrões de literal e também usa um padrão de variável e um padrão OR.
[<Literal>]
let Three = 3
let filter123 x =
match x with
// The following line contains literal patterns combined with an OR pattern.
| 1 | 2 | Three -> printfn "Found 1, 2, or 3!"
// The following line contains a variable pattern.
| var1 -> printfn "%d" var1
for x in 1..10 do filter123 x
Outro exemplo de um padrão literal é um padrão baseado em constantes de enumeração. Você deve especificar o nome do tipo de enumeração quando usa constantes de enumeração.
type Color =
| Red = 0
| Green = 1
| Blue = 2
let printColorName (color:Color) =
match color with
| Color.Red -> printfn "Red"
| Color.Green -> printfn "Green"
| Color.Blue -> printfn "Blue"
| _ -> ()
printColorName Color.Red
printColorName Color.Green
printColorName Color.Blue
Padrões do identificador
Se o padrão for uma cadeia de caracteres que forme um identificador válido, a forma do identificador determinará como o padrão será correspondido. Se o identificador não for mais um caractere único e começar com um caractere maiúsculo, o compilador tentará fazer uma correspondência com o padrão do identificador. O identificador para esse padrão poderá ser um valor marcado com o atributo Literal, um caso união discriminado, um identificador de exceção ou um caso de padrão ativo. Se nenhum identificador correspondente for encontrado, a correspondência falhará e a próxima regra de padrão, o padrão de variável, será comparado à entrada.
Os padrões de união discriminados podem ser casos nomeados simples ou podem ter um valor, ou um tuple que contenha diversos valores. Se houver um valor, você deverá especificar um identificador para o valor. No caso de um tuple, você deve fornecer um padrão de tuple com um identificador para cada elemento de tuple ou um identificador com um nome de campo para um ou mais campos nomeados de união. Consulte os exemplos de código nesta seção para obter exemplos.
O tipo option é uma união discriminada que tem dois casos, Some e None. Um caso (Some) tem um valor, mas o outro (None) é apenas um caso nomeado. Portanto, Some precisa ter uma variável para o valor associado ao caso Some, mas None deve aparecer sozinho. No código a seguir, a variável var1 recebe o valor obtido pela correspondência ao caso Some.
let printOption (data : int option) =
match data with
| Some var1 -> printfn "%d" var1
| None -> ()
No exemplo a seguir, a união discriminada PersonName contêm uma mistura de cadeias de caracteres e os caracteres que representam possíveis formas de nomes. Os casos da união discriminada são FirstOnly, LastOnly e FirstLast.
type PersonName =
| FirstOnly of string
| LastOnly of string
| FirstLast of string * string
let constructQuery personName =
match personName with
| FirstOnly(firstName) -> printf "May I call you %s?" firstName
| LastOnly(lastName) -> printf "Are you Mr. or Ms. %s?" lastName
| FirstLast(firstName, lastName) -> printf "Are you %s %s?" firstName lastName
Para as uniões discriminadas com campos nomeados, use o sinal de igual (=) extrair o valor de um campo nomeado. Por exemplo, considere uma união discriminada com uma declaração como a seguinte.
type Shape =
| Rectangle of height : float * width : float
| Circle of radius : float
Você pode usar os campos nomeados em uma expressão de correspondência de padrão da seguinte maneira.
let matchShape shape =
match shape with
| Rectangle(height = h) -> printfn "Rectangle with length %f" h
| Circle(r) -> printfn "Circle with radius %f" r
O uso do campo nomeado é opcional, portanto, no exemplo anterior, Circle(r) e Circle(radius = r) têm o mesmo efeito.
Ao especificar vários campos, use ponto e vírgula (;) como um separador.
match shape with
| Rectangle(height = h; width = w) -> printfn "Rectangle with height %f and width %f" h w
| _ -> ()
Os padrões ativos permitem que você defina uma correspondência de padrão personalizado mais complexa. Para obter mais informações sobre padrões ativos, consulte Padrões ativos (F#).
O caso em que o identificador é uma exceção é usado em correspondência padrão no contexto de manipuladores de exceção. Para obter informações sobre correspondência de padrões no tratamento de exceções, consulte Exceções: a expressão try...with (F#).
Padrões variáveis
O padrão de variável atribui o valor que está sendo correspondente a um nome de variável, que está disponível em seguida para uso na expressão de execução à direita do símbolo ->. Um padrão variável sozinho corresponde a qualquer entrada, mas os padrões variáveis geralmente aparecem dentro de outros padrões, portanto, ativando estruturas mais complexas, como tuples e matrizes a serem decompostos em variáveis.
O exemplo a seguir demonstra um padrão variável em um padrão da tupla.
let function1 x =
match x with
| (var1, var2) when var1 > var2 -> printfn "%d is greater than %d" var1 var2
| (var1, var2) when var1 < var2 -> printfn "%d is less than %d" var1 var2
| (var1, var2) -> printfn "%d equals %d" var1 var2
function1 (1,2)
function1 (2, 1)
function1 (0, 0)
como Padrão
O padrão as tem uma cláusula as acrescentada a ele. A cláusula as associa o valor correspondente a um nome que pode ser usado na expressão de execução de uma expressão match ou, casos esse padrão seja usado em uma associação let, o nome é adicionado como uma associação ao escopo local.
O exemplo a seguir usa um padrão as.
let (var1, var2) as tuple1 = (1, 2)
printfn "%d %d %A" var1 var2 tuple1
Padrão OR
O padrão OR é usado quando os dados de entrada podem conter vários padrões e você quer executar o mesmo código como resultado. Os tipos de ambos os lados do padrão OR devem ser compatíveis.
O exemplo a seguir demonstra o padrão OR.
let detectZeroOR point =
match point with
| (0, 0) | (0, _) | (_, 0) -> printfn "Zero found."
| _ -> printfn "Both nonzero."
detectZeroOR (0, 0)
detectZeroOR (1, 0)
detectZeroOR (0, 10)
detectZeroOR (10, 15)
Padrão de AND
O padrão AND requer que a entrada corresponda a dois padrões. Os tipos de ambos os lados do padrão AND devem ser compatíveis.
O exemplo a seguir é como o detectZeroTuple mostrado na seção Padrão de Tupla posteriormente neste tópico, mas aqui var1 e var2 são obtidos como valores utilizando o padrão AND.
let detectZeroAND point =
match point with
| (0, 0) -> printfn "Both values zero."
| (var1, var2) & (0, _) -> printfn "First value is 0 in (%d, %d)" var1 var2
| (var1, var2) & (_, 0) -> printfn "Second value is 0 in (%d, %d)" var1 var2
| _ -> printfn "Both nonzero."
detectZeroAND (0, 0)
detectZeroAND (1, 0)
detectZeroAND (0, 10)
detectZeroAND (10, 15)
Padrão Constante
O padrão cons é usado para decompor uma lista no primeiro elemento, o head, e uma lista que contém os elementos restantes, o tail.
let list1 = [ 1; 2; 3; 4 ]
// This example uses a cons pattern and a list pattern.
let rec printList l =
match l with
| head :: tail -> printf "%d " head; printList tail
| [] -> printfn ""
printList list1
Padrão de Lista
O padrão de lista permite que as listas sejam decompostas em um número de elementos. O próprio padrão de lista pode corresponder apenas às listas de um número específico de elementos.
// This example uses a list pattern.
let listLength list =
match list with
| [] -> 0
| [ _ ] -> 1
| [ _; _ ] -> 2
| [ _; _; _ ] -> 3
| _ -> List.length list
printfn "%d" (listLength [ 1 ])
printfn "%d" (listLength [ 1; 1 ])
printfn "%d" (listLength [ 1; 1; 1; ])
printfn "%d" (listLength [ ] )
Padrão de matriz
O padrão de matriz lembra o padrão de lista e pode ser usado para decompor matrizes de um tamanho específico.
// This example uses array patterns.
let vectorLength vec =
match vec with
| [| var1 |] -> var1
| [| var1; var2 |] -> sqrt (var1*var1 + var2*var2)
| [| var1; var2; var3 |] -> sqrt (var1*var1 + var2*var2 + var3*var3)
| _ -> failwith "vectorLength called with an unsupported array size of %d." (vec.Length)
printfn "%f" (vectorLength [| 1. |])
printfn "%f" (vectorLength [| 1.; 1. |])
printfn "%f" (vectorLength [| 1.; 1.; 1.; |])
printfn "%f" (vectorLength [| |] )
Padrão posto entre parênteses
Os parênteses podem ser agrupados em torno dos padrões para obter a associatividade desejada. No exemplo a seguir, parênteses são usados para controlar a associatividade entre o padrão E e um padrão constante.
let countValues list value =
let rec checkList list acc =
match list with
| (elem1 & head) :: tail when elem1 = value -> checkList tail (acc + 1)
| head :: tail -> checkList tail acc
| [] -> acc
checkList list 0
let result = countValues [ for x in -10..10 -> x*x - 4 ] 0
printfn "%d" result
Padrão da tupla
O padrão de tupla corresponde à entrada no formulário de tupla e permite que a tupla seja decomposta em seus elementos constituintes usando variáveis de correspondência de padrão para cada posição na tupla.
O exemplo a seguir demonstra o padrão de tupla e também usa padrões de literal, padrões de variável e o padrão de curinga.
let detectZeroTuple point =
match point with
| (0, 0) -> printfn "Both values zero."
| (0, var2) -> printfn "First value is 0 in (0, %d)" var2
| (var1, 0) -> printfn "Second value is 0 in (%d, 0)" var1
| _ -> printfn "Both nonzero."
detectZeroTuple (0, 0)
detectZeroTuple (1, 0)
detectZeroTuple (0, 10)
detectZeroTuple (10, 15)
Padrão de Gravação
O padrão de registro é usado para decompor registros para extrair os valores dos campos. O padrão não tem que fazer referência a todos os campos do registro; os campos omitidos não participam da correspondência e não são extraídos.
// This example uses a record pattern.
type MyRecord = { Name: string; ID: int }
let IsMatchByName record1 (name: string) =
match record1 with
| { MyRecord.Name = nameFound; MyRecord.ID = _; } when nameFound = name -> true
| _ -> false
let recordX = { Name = "Parker"; ID = 10 }
let isMatched1 = IsMatchByName recordX "Parker"
let isMatched2 = IsMatchByName recordX "Hartono"
Padrão de Curinga
O padrão de curinga é representado pelo caractere de sublinhado (_) e corresponde a qualquer entrada, assim como o padrão de variável, exceto que a entrada é descartada, em vez de atribuída a uma variável. O padrão de curinga geralmente é usado em outros padrões como um espaço reservado para os valores que não são necessários na expressão à direita do símbolo ->. O padrão de curinga também é usado com frequência no fim de uma lista de padrões para corresponder a qualquer entrada sem correspondência. O padrão de curinga é demonstrado em muitos exemplos de código neste tópico. Consulte o código anterior para ver um exemplo.
Padrões que possuem anotações de tipo
Os padrões podem ter anotações de tipo. Esses se comportam como outras anotações de tipo e inferência da guia, como outras anotações de tipo. Os parênteses são necessários em torno das anotações de tipo nos padrões. O código a seguir mostra um padrão que tem uma anotação de tipo.
let detect1 x =
match x with
| 1 -> printfn "Found a 1!"
| (var1 : int) -> printfn "%d" var1
detect1 0
detect1 1
Padrão de teste de tipo
O padrão de teste de tipo é usado para corresponder à entrada com relação a um tipo. Se o tipo de entrada for uma correspondência (ou um tipo derivado) do tipo especificado no padrão, a correspondência terá êxito.
O exemplo a seguir demonstra o padrão de teste de tipo.
open System.Windows.Forms
let RegisterControl(control:Control) =
match control with
| :? Button as button -> button.Text <- "Registered."
| :? CheckBox as checkbox -> checkbox.Text <- "Registered."
| _ -> ()
Padrão zero
O padrão nulo corresponde ao valor nulo que pode aparecer quando você estiver trabalhando com tipos que permitem um valor nulo. Os padrões nulos são frequentemente usados ao interoperar com código de .NET Framework. Por exemplo, o valor de retorno de uma API do .NET API pode ser a entrada para uma expressão match. Você pode controlar o fluxo de programa baseado em se ou não o valor de retorno é zero, e também em outras características do valor retornado. Você pode usar o padrão zero para impedir que os valores nulos propaguem para o restante do seu programa.
O exemplo a seguir usa o padrão nulo e o padrão de variável.
let ReadFromFile (reader : System.IO.StreamReader) =
match reader.ReadLine() with
| null -> printfn "\n"; false
| line -> printfn "%s" line; true
let fs = System.IO.File.Open("..\..\Program.fs", System.IO.FileMode.Open)
let sr = new System.IO.StreamReader(fs)
while ReadFromFile(sr) = true do ()
sr.Close()