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

  1. Patrones Literales: Coinciden con valores específicos.
  2. Patrones de Tuplas: Coinciden con tuplas y descomponen sus elementos.
  3. Patrones de Lista: Coinciden con listas y permiten descomponerlas.
  4. Patrones de Unión Discriminada: Coinciden con tipos de unión discriminada.
  5. 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 variable x.
  • | 1 -> "Uno": Si x es 1, devuelve "Uno".
  • | 2 -> "Dos": Si x 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 un Circle y extrae su radio.
  • Rectangle (width, height) -> sprintf "Un rectángulo de %f x %f" width height: Coincide con un Rectangle 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

Módulo 3: Programación Funcional

Módulo 4: Estructuras de Datos Avanzadas

Módulo 5: Programación Orientada a Objetos en F#

Módulo 6: Programación Asíncrona y Paralela

Módulo 7: Acceso y Manipulación de Datos

Módulo 8: Pruebas y Depuración

Módulo 9: Temas Avanzados

Módulo 10: Aplicaciones Prácticas

© Copyright 2024. Todos los derechos reservados