Coincidencia de patrones
Los patrones son reglas para transformar los datos de entrada. Se usan en F# para comparar datos con una estructura lógica o estructuras, descomponer datos en partes constituyentes o extraer información de los datos de varias maneras.
Observaciones
Los patrones se usan en muchas construcciones de lenguaje, como la expresión match
. Se usan cuando se procesan argumentos para funciones en enlaces de let
, expresiones lambda y en los controladores de excepciones asociados a la expresión try...with
. Para más información, consulte Expresiones de coincidencia, Enlaces let, Expresiones Lambda: Palabra clave fun
y Excepciones: Expresión try...with
.
Por ejemplo, en la expresión match
, el patrón es lo que sigue al símbolo de canalización.
match expression with
| pattern [ when condition ] -> result-expression
...
Cada patrón actúa como regla para transformar la entrada de alguna manera. En la expresión match
, cada patrón se examina a su vez para ver si los datos de entrada son compatibles con el patrón. Si se encuentra una coincidencia, se ejecuta la expresión de resultado. Si no se encuentra una coincidencia, se prueba la siguiente regla de patrón. El elemento opcional cuando la parte condition se explica en Expresiones de coincidencia.
Los patrones admitidos se muestran en la tabla siguiente. En tiempo de ejecución, la entrada se prueba en cada uno de los siguientes patrones en el orden que se muestra en la tabla y los patrones se aplican de forma recursiva, de primero a último, tal como aparecen en el código y de izquierda a derecha para los patrones de cada línea.
Nombre | Descripción | Ejemplo |
---|---|---|
Patrón constante | Cualquier literal numérico, carácter o cadena, una constante de enumeración o un identificador literal definido | 1.0 , "test" , 30 , Color.Red |
Patrón de identificador | Valor de caso de una unión discriminada, una etiqueta de excepción o un caso de patrón activo | Some(x) Failure(msg) |
Patrón variable | identifier | a |
as pattern |
pattern as identifier | (a, b) as tuple1 |
Patrón OR | pattern1 | pattern2 | ([h] | [h; _]) |
Patrón AND | pattern1 & pattern2 | (a, b) & (_, "test") |
Patrón de desventajas | identifier :: list-identifier | h :: t |
Patrón de lista | [ pattern_1; ... ; pattern_n ] | [ a; b; c ] |
Patrón de matriz | [| pattern_1; ..; pattern_n |] | [| a; b; c |] |
Patrón con paréntesis | ( pattern ) | ( a ) |
Patrón de tupla | ( pattern_1, ... , pattern_n ) | ( a, b ) |
Patrón de registro | { identifier1 = pattern_1; ... ; identifier_n = pattern_n } | { Name = name; } |
Patrón de caracteres comodín | _ | _ |
Patrón junto con anotación de tipo | pattern : type | a : int |
Patrón de prueba de tipos | :? type [ as identifier ] | :? System.DateTime as dt |
Patrón NULL | nulo | null |
Patrón nameof | nameof expr | nameof str |
Patrones constantes
Los patrones constantes son literales numéricos, caracteres y cadenas, constantes de enumeración (con el nombre del tipo de enumeración incluido). Una expresión match
que solo tiene patrones constantes se puede comparar con una instrucción case en otros lenguajes. La entrada se compara con el valor literal y el patrón coincide si los valores son iguales. El tipo del literal debe ser compatible con el tipo de entrada.
En el ejemplo siguiente se muestra el uso de patrones literales y también se usa un patrón variable y un patrón 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
Otro ejemplo de un patrón literal es un patrón basado en constantes de enumeración. Debe especificar el nombre del tipo de enumeración al usar constantes de enumeración.
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
Patrones de identificador
Si el patrón es una cadena de caracteres que forma un identificador válido, el formulario del identificador determina cómo coincide el patrón. Si el identificador es mayor que un solo carácter y comienza con un carácter en mayúsculas, el compilador intenta hacer una coincidencia con el patrón de identificador. El identificador de este patrón podría ser un valor marcado con el atributo Literal, un caso de unión discriminado, un identificador de excepción o un caso de patrón activo. Si no se encuentra ningún identificador coincidente, se produce un error en la coincidencia y la siguiente regla de patrón, el patrón de variable, se compara con la entrada.
Los patrones de unión discriminada pueden ser casos con nombre simples o pueden tener un valor o una tupla que contenga varios valores. Si hay un valor, debe especificar un identificador para el valor. En el caso de una tupla, debe proporcionar un patrón de tupla con un identificador para cada elemento de la tupla o un identificador con un nombre de campo para uno o varios campos de unión con nombre. Consulte los ejemplos de código de esta sección para obtener ejemplos.
El tipo option
es una unión discriminada que tiene dos casos Some
y None
. Un caso (Some
) tiene un valor, pero el otro (None
) es solo un caso con nombre. Por lo tanto, Some
debe tener una variable para el valor asociado al Some
caso, pero None
debe aparecer por sí mismo. En el código siguiente, a la variable var1
se le asigna el valor que se obtiene mediante la coincidencia con el Some
caso.
let printOption (data : int option) =
match data with
| Some var1 -> printfn "%d" var1
| None -> ()
En el ejemplo siguiente, la unión discriminante PersonName
contiene una mezcla de cadenas y caracteres que representan posibles formas de nombres. Los casos de la unión discriminada son FirstOnly
, LastOnly
y 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 uniones discriminadas que tienen campos con nombre, use el signo igual (=) para extraer el valor de un campo con nombre. Por ejemplo, considere una unión discriminada con una declaración como la siguiente.
type Shape =
| Rectangle of height : float * width : float
| Circle of radius : float
Puede usar los campos con nombre en una expresión de coincidencia de patrones como se indica a continuación.
let matchShape shape =
match shape with
| Rectangle(height = h) -> printfn $"Rectangle with length %f{h}"
| Circle(r) -> printfn $"Circle with radius %f{r}"
El uso del campo con nombre es opcional, por lo que en el ejemplo anterior, tanto Circle(r)
como Circle(radius = r)
tienen el mismo efecto.
Al especificar varios campos, use el punto y coma (;) como separador.
match shape with
| Rectangle(height = h; width = w) -> printfn $"Rectangle with height %f{h} and width %f{w}"
| _ -> ()
Los patrones activos te permiten definir coincidencias más complejas para patrones personalizados. Para obtener más información sobre los patrones activos, consulte Patrones activos.
El caso en el que el identificador es una excepción se usa en la coincidencia de patrones en el contexto de los controladores de excepciones. Para más información sobre la coincidencia de patrones en el control de excepciones, consulte Excepciones: Expresióntry...with
.
Patrones de variable
El patrón de variable asigna el valor coincidente a un nombre de variable, que luego está disponible para usar en la expresión de ejecución situada a la derecha del símbolo ->
. Un patrón de variable coincide solo con cualquier entrada, pero los patrones de variable a menudo aparecen dentro de otros patrones, por lo que permiten que las estructuras más complejas, como las tuplas y las matrices se descomponan en variables.
En el ejemplo siguiente se muestra un patrón de variable dentro de un patrón de 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)
Patrón "as"
El patrón as
es un patrón que tiene una cláusula as
anexada. La cláusula as
enlaza el valor coincidente a un nombre que se puede usar en la expresión de ejecución de una expresión de match
o, en el caso de que se use este patrón en un enlace de let
, el nombre se agrega como un enlace al ámbito local.
En el ejemplo siguiente se usa un patrón as
.
let (var1, var2) as tuple1 = (1, 2)
printfn "%d %d %A" var1 var2 tuple1
Patrón OR
El patrón OR se usa cuando los datos de entrada pueden coincidir con varios patrones y desea ejecutar el mismo código como resultado. Los tipos de ambos lados del patrón OR deben ser compatibles.
En el ejemplo siguiente se muestra el patrón 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)
Patrón AND
El patrón AND requiere que la entrada coincida con dos patrones. Los tipos de ambos lados del patrón AND deben ser compatibles.
El ejemplo siguiente es como detectZeroTuple
que se muestra en la sección Patrón de tupla más adelante en este tema, pero aquí se obtienen tanto var1
como var2
como valores mediante el patrón 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)
Patrón consecutivo
El patrón consecutivo se usa para descomponer una lista en el primer elemento, denominado cabeza, y una lista que contiene los elementos restantes, o cola.
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
Patrón de lista
El patrón de lista permite que las listas se descomponan en varios elementos. El propio patrón de lista solo puede coincidir con listas de un 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 [ ] )
Patrón de matriz
El patrón de matriz es similar al patrón de lista y se puede usar para descomponer matrices de una longitud específica.
// 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 (sprintf "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 [| |] )
Patrón entre paréntesis
Los paréntesis se pueden agrupar en torno a patrones para lograr la asociatividad deseada. En el ejemplo siguiente, los paréntesis se usan para controlar la asociatividad entre un patrón AND y un patrón cons.
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
Patrón de tupla
El patrón de tupla coincide con la entrada en forma de tupla y permite que la tupla se descomponga en sus elementos constituyentes mediante variables de coincidencia de patrones para cada posición de la tupla.
En el ejemplo siguiente se muestra el patrón de tupla y también se usan patrones literales, patrones de variables y el patrón de caracteres comodín.
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)
Patrón de registro
El patrón de registro se usa para descomponer los registros para extraer los valores de los campos. El patrón no tiene que hacer referencia a todos los campos del registro; los campos omitidos simplemente no participan en la coincidencia y no se extraen.
// 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"
Patrón de caracteres comodín
El patrón comodín se representa mediante el carácter de subrayado (_
) y coincide con cualquier entrada, al igual que el patrón de variable, excepto que la entrada se descarta en lugar de asignarse a una variable. El patrón de caracteres comodín se usa a menudo dentro de otros patrones como marcador de posición para los valores que no son necesarios en la expresión a la derecha del símbolo ->
. El patrón de caracteres comodín también se usa con frecuencia al final de una lista de patrones para hacer que coincida con alguna entrada que no tenga coincidencia. El patrón de caracteres comodín se muestra en muchos ejemplos de código de este tema. Consulte el código anterior para obtener un ejemplo.
Patrones que tienen anotaciones de tipo
Los patrones pueden tener anotaciones de tipo. Se comportan como otras anotaciones de tipo y guían la inferencia como otras anotaciones de tipo. Los paréntesis son necesarios en torno a las anotaciones de tipo en los patrones. El siguiente código muestra un patrón que incluye una anotación de tipo.
let detect1 x =
match x with
| 1 -> printfn "Found a 1!"
| (var1 : int) -> printfn "%d" var1
detect1 0
detect1 1
Patrón de prueba de tipos
El patrón de prueba de tipo se usa para hacer coincidir la entrada con un tipo. Si el tipo de entrada coincide con el tipo especificado en el patrón (o es un tipo derivado de este), la coincidencia se realiza correctamente.
En el siguiente ejemplo se muestra el patrón de prueba de tipos.
open System.Windows.Forms
let RegisterControl(control:Control) =
match control with
| :? Button as button -> button.Text <- "Registered."
| :? CheckBox as checkbox -> checkbox.Text <- "Registered."
| _ -> ()
Si solo comprueba si un identificador es de un tipo derivado determinado, no necesita la parte as identifier
del patrón, como se muestra en el ejemplo siguiente:
type A() = class end
type B() = inherit A()
type C() = inherit A()
let m (a: A) =
match a with
| :? B -> printfn "It's a B"
| :? C -> printfn "It's a C"
| _ -> ()
Patrón null
El patrón NULL coincide con el valor NULL que puede aparecer cuando se trabaja con tipos que permiten un valor NULL. Los patrones NULL se usan con frecuencia al interoperar con código de .NET Framework. Por ejemplo, el valor devuelto de una API de .NET podría ser la entrada en una expresión de match
. Puede controlar el flujo del programa en función de si el valor devuelto es NULL y también en otras características del valor devuelto. Puede usar el patrón NULL para evitar que los valores NULL se propague al resto del programa.
En el ejemplo siguiente se usa el patrón NULL y el patrón de variable.
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()
También se recomienda un patrón NULL para las funcionalidades de nulabilidad de F# 9:
let len (str: string | null) =
match str with
| null -> -1
| s -> s.Length
Del mismo modo, puede utilizar nuevos patrones dedicados relacionados con la nulabilidad:
let let str = // str is inferred to be `string | null`
match str with
| Null -> -1
| NonNull (s: string) -> s.Length
Patrón nameof
El patrón nameof
coincide con una cadena cuando su valor es igual a la expresión que sigue a la palabra clave nameof
. por ejemplo:
let f (str: string) =
match str with
| nameof str -> "It's 'str'!"
| _ -> "It is not 'str'!"
f "str" // matches
f "asdf" // does not match
Consulte el operador nameof
para obtener información sobre de qué puede tomar un nombre.