Reconocimiento de patrones¶
El reconocimiento de patrones es una herramienta que provee la programación funcional para validar que un dato cumple una condición, evitando tener que asignar valores y validar condiciones en varias líneas de código.
Ejemplo inicial sin reconocimiento de patrones¶
def sumaPares(l:List[Int]):Int = {
if (l.isEmpty) 0
else{
if (l.head % 2 == 0) l.head+sumaPares(l.tail)
else sumaPares(l.tail)
}
}
Ejemplo con reconocimiento de patrones básico¶
def sumaPares(l:List[Int]):Int = {
l match {
case Nil => 0 // Patrón para lista vacía
case h :: t => { // Patrón para descomponer lista en cabeza (h) y cola (t)
if (h % 2 == 0) h + sumaPares(t)
else sumaPares(t)
}
case _ => throw new Exception("Debe ingresar una lista") // Patrón comodín para cualquier otro caso
}
}
Ejemplo con guardias (guards) en patrones¶
def sumaPares(l:List[Int]):Int = {
l match {
case Nil => 0 // Lista vacía
case h :: t if (h % 2) == 0 => h + sumaPares(t) // Patrón con condición (guardia)
case h :: t => sumaPares(t) // Lista no vacía sin condición específica
// Nota: El caso comodín (_) se ha eliminado porque los patrones anteriores cubren todas las posibilidades
}
}
Patrones comunes para listas¶
En el caso de listas, generalmente necesitamos capturar dos situaciones:
- Nil - Lista vacía
- h :: t - Lista no vacía, con cabeza (h) y cola (t)
Sin embargo, somos libres de capturar varios patrones más específicos:
- List() - Lista vacía (equivalente a Nil)
- List(x) - Lista con un elemento
- x :: Nil - Lista con un elemento (equivalente a List(x))
- List(x,y) - Lista con dos elementos
- x :: y :: Nil - Lista con dos elementos (equivalente a List(x,y))
Patrones para tuplas¶
Para tuplas como (x,y) o (1,2):
tupla match {
case (0,0) => "respuesta" // Tupla específica (0,0)
case (1,1) => "respuesta" // Tupla específica (1,1)
case (x,0) => "respuesta" // Tupla donde el segundo elemento es 0
// ... otros casos
case _ => "respuesta por defecto" // Cualquier otra tupla
}
Ventajas del reconocimiento de patrones¶
-
Evita asignaciones explícitas:
h :: tes equivalente a:Pero más conciso y seguro (evita errores con listas vacías).val h = l.head val t = l.tail -
Facilita la programación: Reduce la verbosidad del código y mejora la legibilidad.
-
Útil con esquemas polimórficos en clases:
abstract class Animal(nombre:String, edad:Int) class Mamifero(tipo:String, num_patas:Int) extends Animal(nombre:String, edad:Int) class Reptil(color:String) extends Animal(nombre:String, edad:Int)
Puedo hacer lo siguiente:
obj match {
case Mamifero(tipo, edad) => ... // Patrón para objetos de tipo Mamifero
case Reptil(color) => ... // Patrón para objetos de tipo Reptil
}
Conceptos teóricos importantes¶
Reconocimiento de patrones (pattern matching) es una característica fundamental de los lenguajes funcionales que permite descomponer estructuras de datos de forma segura y declarativa. A diferencia de la programación imperativa donde se accede a los componentes de una estructura mediante métodos o propiedades (como .head o .tail), el pattern matching verifica primero la forma de la estructura y luego extrae sus componentes.
Patrones comunes:
- Patrones constantes: Coinciden con valores específicos (Nil, 0, "texto")
- Patrones de variable: Capturan valores en variables (x, h, t)
- Patrones de constructor: Descomponen estructuras (h :: t, Mamifero(tipo, edad))
- Patrones comodín: Coinciden con cualquier valor (_)
- Patrones con guardias: Agregan condiciones a los patrones (case h :: t if h % 2 == 0)
Exhaustividad: El compilador de Scala puede verificar si los casos del pattern matching cubren todas las posibilidades, ayudando a prevenir errores en tiempo de ejecución.
Tabla de resumen¶
| Concepto | Descripción | Ejemplo |
|---|---|---|
| Pattern Matching | Mecanismo para descomponer y validar estructuras de datos | valor match { case patrón => resultado } |
| Patrón Nil | Representa lista vacía en Scala | case Nil => "lista vacía" |
| Patrón :: (cons) | Descompone lista en cabeza y cola | case h :: t => procesar(h, t) |
| Patrón comodín | Coincide con cualquier valor | case _ => "valor por defecto" |
| Guardias | Condiciones adicionales en patrones | case x if x > 0 => "positivo" |
| Patrones de tupla | Descomposición de tuplas | case (x, y) => x + y |
| Patrones de case class | Descomposición de objetos case class | case Persona(nombre, edad) => ... |
| Exhaustividad | Propiedad que asegura cubrir todos los casos posibles | Verificación en tiempo de compilación |
Comentarios adicionales¶
- El pattern matching en Scala es más poderoso que las sentencias
switchde otros lenguajes, ya que permite descomponer estructuras complejas y no solo comparar valores simples. - Los patrones se evalúan en orden, por lo que los casos más específicos deben ir antes que los más generales.
- El pattern matching es fundamental para el manejo seguro de opciones (
Option,Some,None) en Scala. No usar esto, no es funcional - En programación funcional, el pattern matching se prefiere sobre el uso de
if-elseanidados para mejorar la claridad y mantenibilidad del código. - Los patrones pueden anidarse, permitiendo descomposiciones complejas en una sola expresión.
- El compilador de Scala puede optimizar el pattern matching, convirtiéndolo en tablas de salto eficientes en tiempo de ejecución.