Coincidencia de modelos [F#]
Los modelos son reglas para transformar los datos de entrada. Se utilizan en todo el lenguaje F# para comparar los datos con una o más estructuras lógicas, descomponer los datos en sus partes constituyentes o extraer información de los datos de varias maneras.
Comentarios
Los modelos se utilizan en muchas construcciones de lenguaje, como la expresión match. Se utilizan cuando se procesan argumentos para las funciones en los enlaces let, las expresiones lambda, y en los controladores de excepciones asociados a la expresión try...with. Para obtener más información, vea Expresiones match (F#), Enlaces let (F#), Expresiones lambda: la palabra clave fun (F#) y Excepciones: la expresión try...with (F#).
Por ejemplo, en la expresión match, pattern es lo que aparece detrás de la barra vertical.
match expression with
| pattern [ cuando condition ] -> result-expression
...
Cada modelo actúa como regla para transformar la entrada de alguna manera. En la expresión match, se examina cada modelo a su vez para comprobar si los datos de entrada son compatibles con el modelo. Si se encuentra una coincidencia, se ejecuta la expresión de resultado. Si no se encuentra ninguna coincidencia, se prueba la regla del modelo siguiente. La parte when condition opcional se explica en Expresiones match (F#).
Los modelos admitidos se muestran en la tabla siguiente. En tiempo de ejecución, la entrada se prueba con respecto a cada uno de los modelos siguientes en el orden en que se muestran en la tabla. A continuación, los modelos se aplican de forma recursiva, del primero a último conforme aparecen en el código, y de izquierda a derecha para los modelos de cada línea.
Nombre |
Descripción |
Ejemplo |
---|---|---|
Modelo de constante |
Cualquier literal numérico, de carácter o de cadena, una constante de enumeración o un identificador literal definido |
1.0, "test", 30, Color.Red |
Patrón de identificador |
Un valor de caso de una unión discriminada, una etiqueta de excepción o un caso de modelo activo |
Some(x) Failure(msg) |
Modelo de variable |
identifier |
a |
Modelo de as |
pattern as identifier |
(a, b) as tuple1 |
Modelo de OR |
pattern1 | pattern2 |
([h] | [h; _]) |
modelo AND |
pattern1 & pattern2 |
(a, b) & (_, "test") |
Modelo de cons |
identifier :: list-identifier |
h :: t |
Modelo de lista |
[ pattern_1; ... ; pattern_n ] |
[ a; b; c ] |
Modelo de matriz |
[| pattern_1; ..; pattern_n ] |
[| a; b; c |] |
Modelo entre paréntesis |
( pattern ) |
( a ) |
Modelo de tupla |
( pattern_1, ... , pattern_n ) |
( a, b ) |
Modelo de registro |
{ identifier1 = pattern_1; ... ; identifier_n = pattern_n } |
{ Name = name; } |
Modelo de carácter comodín |
_ |
_ |
Modelo junto con anotación de tipo |
pattern : type |
a : int |
Modelo de prueba de tipo |
:? type [ as identifier ] |
:? System.DateTime as dt |
Modelo de NULL |
null |
null |
Modelos de constante
Los modelos de constantes son literales numéricos, de carácter y de cadena, y constantes de enumeración (incluido el nombre de tipo de enumeración). Una expresión match que solo tiene modelos de constante se puede comparar con la instrucción case de otros lenguajes. La entrada se compara con el valor literal y el modelo coincide si los valores son iguales. El tipo del literal debe ser compatible con el tipo de la entrada.
En el ejemplo siguiente se muestra el uso de los modelos de literal y, además, se utiliza un modelo de variable y un modelo de 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 modelo de literal es un modelo basado en constantes de enumeración. Cuando se utilizan constantes de enumeración, hay que especificar el nombre del tipo 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 modelo es una cadena de caracteres que forma un identificador válido, la forma del identificador determina cómo se efectúa la coincidencia de modelos. Si el identificador tiene más de un solo carácter y comienza con un carácter en mayúscula, el compilador intenta hallar una coincidencia con el modelo de identificador. El identificador de este modelo 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, la coincidencia no se realiza y se compara con la entrada la regla del modelo siguiente, el modelo de variable.
Los modelos de unión discriminada pueden ser casos con nombre simples o bien tener un valor o una tupla que contenga varios valores. Si hay un valor, debe especificar un identificador para el valor. En en caso de una tupla, se 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 más campos de unión con nombre. Vea los ejemplos de código en 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) simplemente es un caso con nombre. Por consiguiente, Some necesita tener una variable para el valor asociado al caso Some, pero None debe aparecer por sí solo. En el código siguiente, se proporciona a la variable var1 el valor que se obtiene al hallar la coincidencia 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
En las uniones discriminadas que tienen campos de nombre, utilice el signo igual (=) para extraer el valor de un campo de 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 sigue.
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 especifica varios campos, utilice punto y coma (;) como separador.
match shape with
| Rectangle(height = h; width = w) -> printfn "Rectangle with height %f and width %f" h w
| _ -> ()
Los modelos activos permiten definir coincidencias de modelos personalizadas más complejas. Para obtener más información acerca de los patrones activos, vea Modelos activos (F#).
En la coincidencia de modelos, el caso en que el identificador es una excepción se utiliza en el contexto de los controladores de excepciones. Para obtener información acerca de la coincidencia de modelos en el control de excepciones, vea Excepciones: la expresión try...with (F#).
Modelos de variable
El modelo de variable asigna el valor cuya coincidencia se va a hallar a un nombre de variable que, a continuación, está disponible para utilizarla en la expresión de ejecución situada a la derecha del símbolo ->. Un modelo de variable individual coincide con cualquier entrada; sin embargo, los modelos de variable suelen aparecer dentro de otros modelos, a fin de permitir la descomposición en variables de estructuras más complejas, como las tuplas y las matrices.
En el ejemplo siguiente se muestra un modelo de variable dentro de un modelo 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)
Modelo de as
El modelo de as es aquel que lleva anexada una cláusula as. La cláusula as enlaza el valor coincidente a un nombre que se puede utilizar en la expresión de ejecución de una expresión match; o bien, si este modelo se utiliza para un enlace let, el nombre se agrega como enlace al ámbito local.
En el ejemplo siguiente se utiliza un modelo de as.
let (var1, var2) as tuple1 = (1, 2)
printfn "%d %d %A" var1 var2 tuple1
Modelo de OR
El modelo de OR se utiliza cuando los datos de entrada pueden coincidir con varios modelos y se desea ejecutar el mismo código como resultado. Los tipos de ambos lados del modelo de OR deben ser compatibles.
En el siguiente ejemplo se muestra el modelo de 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)
Modelo de AND
El modelo de AND exige que la entrada coincida con dos modelos. Los tipos de ambos lados del modelo de AND deben ser compatibles.
El ejemplo siguiente es como detectZeroTuple, que se muestra en la sección Modelo de tupla que aparece más adelante en este mismo tema, pero en este caso tanto var1 como var2 se obtienen como valores utilizando el modelo de 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)
Modelo de cons
El modelo de cons se utiliza para descomponer una lista en el primer elemento, el encabezado, y en una lista que contiene los elementos restantes, la 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
Modelo de lista
El modelo de lista permite descomponer las listas en varios elementos. El propio modelo de lista únicamente puede hallar coincidencias con listas de un número concreto 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 [ ] )
Modelo de matriz
El modelo de matriz se parece al modelo de lista y se puede utilizar para descomponer matrices de una longitud concreta.
// 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 [| |] )
Modelo entre paréntesis
Los paréntesis pueden agruparse en torno a los modelos para lograr la asociatividad deseada. En el ejemplo siguiente, se utilizan paréntesis para controlar la asociatividad entre un modelo de AND y un modelo de 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
Modelo de tupla
El modelo de tupla halla coincidencias con entradas en forma de tupla y permite descomponer la tupla en sus elementos constituyentes utilizando variables de coincidencia de modelos para cada posición de la tupla.
En el ejemplo siguiente se muestra el modelo de tupla y, además, se utilizan modelos de literal, modelos de variable y un modelo de carácter 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)
Modelo de registro
El modelo de registro se utiliza para descomponer los registros a fin de extraer los valores de los campos. El modelo no tiene que hacer referencia a todos los campos del registro; simplemente, los campos omitidos no toman parte 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"
Modelo de carácter comodín
El patrón de caracteres comodín se representa mediante el carácter de subrayado (_) y coincide con cualquier entrada, exactamente igual que el patrón variable, con la salvedad de que la entrada se descarta en lugar de asignarla a una variable. El modelo de carácter comodín se utiliza a menudo dentro de otros modelos como marcador de posición para los valores que no se necesitan en la expresión situada a la derecha del símbolo ->. El modelo de carácter comodín también se suele usar al final de una lista de modelos, a fin de hallar las coincidencias con cualquier entrada no coincidente. El modelo del carácter comodín se muestra en muchos de los ejemplos de código de este tema. Vea el código anterior para obtener un ejemplo.
Modelos que tienen anotaciones de tipo
Los modelos pueden tener anotaciones de tipo. Estas se comportan como cualquier otra anotación de tipo y orientan la inferencia como las demás anotaciones de tipo. Se necesitan paréntesis en torno a las anotaciones de tipo en los modelos. En el código siguiente se muestra un modelo 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
Modelo de prueba de tipo
El modelo de prueba de tipo se utiliza para comparar 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 él, se establece la coincidencia.
En el siguiente ejemplo se muestra el modelo de prueba 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."
| _ -> ()
Modelo de NULL
El modelo de NULL coincide con el valor NULL que puede aparecer cuando se trabaja con tipos que permiten el valor NULL. Los modelos de NULL se utilizan con frecuencia cuando se interopera con código de .NET Framework. Por ejemplo, el valor devuelto de una API de .NET puede ser la entrada para una expresión match. Se puede controlar el flujo de programa basándose en el valor devuelto es NULL, así como en otras características del valor devuelto. El modelo de NULL se puede utilizar para evitar que los valores NULL se propaguen al resto del programa.
En el ejemplo siguiente se utilizan el modelo de NULL y el modelo 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()