La coincidencia de patrones es una característica poderosa y fundamental en F# que permite descomponer datos y tomar decisiones basadas en su estructura. Es similar a un switch
o case
en otros lenguajes, pero mucho más expresiva y flexible.
Conceptos Clave
- Patrones Literales: Coinciden con valores específicos.
- Patrones de Tuplas: Coinciden con tuplas y descomponen sus elementos.
- Patrones de Lista: Coinciden con listas y permiten descomponerlas.
- Patrones de Unión Discriminada: Coinciden con tipos de unión discriminada.
- Patrones de Guardas: Añaden condiciones adicionales a los patrones.
Sintaxis Básica
La coincidencia de patrones se realiza principalmente con la expresión match ... with
. Aquí hay un ejemplo básico:
let describeNumber x = match x with | 1 -> "Uno" | 2 -> "Dos" | _ -> "Otro número" printfn "%s" (describeNumber 1) // Salida: Uno printfn "%s" (describeNumber 3) // Salida: Otro número
Explicación del Código
match x with
: Inicia la coincidencia de patrones sobre la variablex
.| 1 -> "Uno"
: Six
es 1, devuelve "Uno".| 2 -> "Dos"
: Six
es 2, devuelve "Dos".| _ -> "Otro número"
: El guion bajo_
es un comodín que coincide con cualquier valor no especificado anteriormente.
Patrones Literales
Los patrones literales coinciden con valores específicos, como se muestra en el ejemplo anterior.
Patrones de Tuplas
Los patrones de tuplas permiten descomponer tuplas en sus componentes:
let describeTuple t = match t with | (1, "a") -> "Uno y a" | (2, "b") -> "Dos y b" | _ -> "Otra tupla" printfn "%s" (describeTuple (1, "a")) // Salida: Uno y a printfn "%s" (describeTuple (3, "c")) // Salida: Otra tupla
Explicación del Código
(1, "a") -> "Uno y a"
: Si la tupla es(1, "a")
, devuelve "Uno y a".(2, "b") -> "Dos y b"
: Si la tupla es(2, "b")
, devuelve "Dos y b"._ -> "Otra tupla"
: Coincide con cualquier otra tupla.
Patrones de Lista
Los patrones de lista permiten descomponer listas en sus elementos:
let describeList lst = match lst with | [] -> "Lista vacía" | [x] -> sprintf "Una lista con un solo elemento: %d" x | [x; y] -> sprintf "Una lista con dos elementos: %d y %d" x y | _ -> "Una lista con más de dos elementos" printfn "%s" (describeList []) // Salida: Lista vacía printfn "%s" (describeList [1]) // Salida: Una lista con un solo elemento: 1 printfn "%s" (describeList [1; 2]) // Salida: Una lista con dos elementos: 1 y 2 printfn "%s" (describeList [1; 2; 3]) // Salida: Una lista con más de dos elementos
Explicación del Código
[] -> "Lista vacía"
: Coincide con una lista vacía.[x] -> sprintf "Una lista con un solo elemento: %d" x
: Coincide con una lista de un solo elemento.[x; y] -> sprintf "Una lista con dos elementos: %d y %d" x y
: Coincide con una lista de dos elementos._ -> "Una lista con más de dos elementos"
: Coincide con cualquier otra lista.
Patrones de Unión Discriminada
Las uniones discriminadas son tipos que pueden ser uno de varios casos. La coincidencia de patrones es ideal para trabajar con ellas:
type Shape = | Circle of float | Rectangle of float * float let describeShape shape = match shape with | Circle radius -> sprintf "Un círculo con radio %f" radius | Rectangle (width, height) -> sprintf "Un rectángulo de %f x %f" width height printfn "%s" (describeShape (Circle 5.0)) // Salida: Un círculo con radio 5.000000 printfn "%s" (describeShape (Rectangle (4.0, 6.0))) // Salida: Un rectángulo de 4.000000 x 6.000000
Explicación del Código
Circle radius -> sprintf "Un círculo con radio %f" radius
: Coincide con unCircle
y extrae su radio.Rectangle (width, height) -> sprintf "Un rectángulo de %f x %f" width height
: Coincide con unRectangle
y extrae su ancho y alto.
Patrones de Guardas
Las guardas permiten añadir condiciones adicionales a los patrones:
let describeNumberWithGuard x = match x with | n when n > 0 -> "Número positivo" | n when n < 0 -> "Número negativo" | _ -> "Cero" printfn "%s" (describeNumberWithGuard 5) // Salida: Número positivo printfn "%s" (describeNumberWithGuard -3) // Salida: Número negativo printfn "%s" (describeNumberWithGuard 0) // Salida: Cero
Explicación del Código
n when n > 0 -> "Número positivo"
: Coincide con números positivos.n when n < 0 -> "Número negativo"
: Coincide con números negativos._ -> "Cero"
: Coincide con cero.
Ejercicios Prácticos
Ejercicio 1: Coincidencia de Patrones con Tuplas
Escribe una función describePoint
que tome una tupla (x, y)
y devuelva una descripción de la posición del punto en el plano cartesiano.
let describePoint (x, y) = match (x, y) with | (0, 0) -> "Origen" | (x, 0) -> sprintf "En el eje X en %d" x | (0, y) -> sprintf "En el eje Y en %d" y | (x, y) -> sprintf "En el punto (%d, %d)" x y // Pruebas printfn "%s" (describePoint (0, 0)) // Salida: Origen printfn "%s" (describePoint (3, 0)) // Salida: En el eje X en 3 printfn "%s" (describePoint (0, 4)) // Salida: En el eje Y en 4 printfn "%s" (describePoint (2, 3)) // Salida: En el punto (2, 3)
Ejercicio 2: Coincidencia de Patrones con Listas
Escribe una función sumList
que tome una lista de enteros y devuelva la suma de sus elementos utilizando coincidencia de patrones.
let rec sumList lst = match lst with | [] -> 0 | x::xs -> x + sumList xs // Pruebas printfn "%d" (sumList []) // Salida: 0 printfn "%d" (sumList [1; 2; 3]) // Salida: 6 printfn "%d" (sumList [10; -2; 5]) // Salida: 13
Ejercicio 3: Coincidencia de Patrones con Uniones Discriminadas
Define una unión discriminada TrafficLight
con los casos Red
, Yellow
y Green
. Escribe una función nextLight
que tome un TrafficLight
y devuelva el siguiente estado del semáforo.
type TrafficLight = | Red | Yellow | Green let nextLight light = match light with | Red -> Green | Green -> Yellow | Yellow -> Red // Pruebas printfn "%A" (nextLight Red) // Salida: Green printfn "%A" (nextLight Green) // Salida: Yellow printfn "%A" (nextLight Yellow) // Salida: Red
Conclusión
La coincidencia de patrones en F# es una herramienta poderosa que permite descomponer y analizar datos de manera clara y concisa. Desde patrones literales hasta uniones discriminadas, esta característica es esencial para escribir código funcional y expresivo en F#. Asegúrate de practicar con diferentes tipos de patrones para dominar esta técnica fundamental.
Curso de Programación en F#
Módulo 1: Introducción a F#
Módulo 2: Conceptos Básicos
- Tipos de Datos y Variables
- Funciones e Inmutabilidad
- Coincidencia de Patrones
- Colecciones: Listas, Arreglos y Secuencias
Módulo 3: Programación Funcional
- Funciones de Orden Superior
- Recursión
- Encadenamiento y Composición
- Aplicación Parcial y Currificación
Módulo 4: Estructuras de Datos Avanzadas
Módulo 5: Programación Orientada a Objetos en F#
- Clases y Objetos
- Herencia e Interfaces
- Mezclando Programación Funcional y Orientada a Objetos
- Módulos y Espacios de Nombres
Módulo 6: Programación Asíncrona y Paralela
- Flujos de Trabajo Asíncronos
- Biblioteca de Tareas Paralelas
- MailboxProcessor y Agentes
- Patrones de Concurrencia
Módulo 7: Acceso y Manipulación de Datos
Módulo 8: Pruebas y Depuración
- Pruebas Unitarias con NUnit
- Pruebas Basadas en Propiedades con FsCheck
- Técnicas de Depuración
- Perfilado de Rendimiento