Coincidencia de patrones

Los patrones son reglas para transformar los datos de entrada. Se utilizan en F# para comparar datos con una o varias estructuras lógicas, descomponer datos en sus partes constituyentes o extraer información de los datos de varias formas.

Comentarios

Los patrones se usan en muchas construcciones del lenguaje, como la expresión match. Se usan cuando se procesan argumentos para las funciones de enlaces 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 una 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 resultante. En caso contrario, se prueba la siguiente regla de patrón. El elemento opcional cuando la parte condition se explica en Expresiones de coincidencia.

En la tabla siguiente se muestran los patrones admitidos. En tiempo de ejecución, la entrada se prueba en cada uno de los siguientes patrones en el orden indicado en la tabla y los patrones se aplican de forma recursiva, del primero al último, tal como aparecen en el código y de izquierda a derecha en los patrones de cada línea.

Nombre Descripción Ejemplo
Patrón de constante Cualquier literal numérico, de caracteres, de cadena, y una constante de enumeración o un identificador de 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 modelo activo Some(x)

Failure(msg)
Patrón de variable identifier a
Patrón as patrón como identificador (a, b) as tuple1
Patrón OR pattern1 | pattern2 ([h] | [h; _])
Patrón AND pattern1 & pattern2 (a, b) & (_, "test")
Patrón consecutivo 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 entre 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 null null
Patrón nameof nameof expr nameof str

Patrones de constantes

Los patrones de constantes son literales numéricos, de caracteres y de cadena, y constantes de enumeración (con el nombre del tipo de enumeración incluido). Una expresión match que solo tiene patrones de constantes se puede comparar con una instrucción case de 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 de variable y un patrón O.

[<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, la forma del identificador determina cómo coincide el patrón. Si el identificador tiene más de un solo carácter y comienza por un carácter en mayúsculas, el compilador intenta establecer 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 discriminada, un identificador de excepción o un caso de modelo 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 este. 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 ver algunos 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 caso Some, pero None debe aparecer por sí mismo. En el código siguiente, la variable var1 recibe el valor que se obtiene haciendo coincidir con el caso Some.

let printOption (data : int option) =
    match data with
    | Some var1  -> printfn "%d" var1
    | None -> ()

En el ejemplo siguiente, la unión discriminada PersonName contiene una combinación 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, Circle(r) y Circle(radius = r) tienen el mismo efecto.

Cuando especifique 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 permiten definir coincidencias de patrones personalizadas más complejas. Para 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 que se hace coincidir con un nombre de variable, el cual está disponible para su uso en la expresión de ejecución a la derecha del símbolo ->. Un patrón de variable por sí solo coincide con cualquier entrada, pero los patrones de variables a menudo aparecen dentro de otros patrones, lo que permite que las estructuras más complejas, como las tuplas y las matrices, se descompongan 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 con un nombre que se puede usar en la expresión de ejecución de una expresión match o, en el caso de que se use este patrón en un enlace let, el nombre se agrega como enlace al ámbito local.

El ejemplo siguiente utiliza 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 siguiente ejemplo 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 el de detectZeroTuple que se muestra en la sección Patrón de tupla más adelante en este tema pero, en este caso, var1 y var2 se obtienen 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 descompongan 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 consecutivo.

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 y 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 de caracteres comodín se representa mediante el carácter de subrayado (_) y coincide con todas las entradas, al igual que el patrón de variable, excepto en 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 ver un ejemplo.

Patrones que tienen anotaciones de tipo

Los patrones pueden tener anotaciones de tipo. Se comportan y guían las inferencias como otras anotaciones de tipo. Se requiere que las anotaciones de tipo de los patrones vayan entre paréntesis. El código siguiente muestra un patrón que tiene 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 tipos se usa para hacer coincidir la entrada con un tipo. Si el tipo de entrada coincide con (o es un tipo derivado de) el tipo especificado en el patrón, 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 a una expresión match. Puede controlar el flujo del programa en función de si el valor devuelto es null y también de otras características del valor devuelto. Puede usar el patrón null para evitar que los valores null se propaguen 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()

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.

Consulte también