Reconocimiento de patrones sobre listas¶
Una lista está compuesta por cabeza (head) y cola (tail), donde la cola es a su vez una lista, lo que la convierte en una definición recursiva. La lista tiene dos casos fundamentales:
- Nil: Caso de lista vacía (equivalente a
List()) - x :: xs: Patrón de cabeza (x) y cola (xs), donde
::es el operador de construcción de listas (cons)
Podemos usar reconocimiento de patrones en listas validando primero el caso de vacío y luego los demás:
// Función genérica que utiliza pattern matching sobre listas
def funcion[T](lst: List[T]): T = {
lst match { // Inicio del pattern matching sobre la lista
case Nil => ... // Caso base: lista vacía
case x :: xs => ... // Caso recursivo: x es la cabeza, xs es la cola
}
}
Esto es equivalente a usar métodos de la lista directamente:
// Versión alternativa usando métodos de la clase List
def funcion[T](lst: List[T]): T = {
if (lst.isEmpty) ... // Verificación explícita de lista vacía
else {
// lst.head para extraer la cabeza y lst.tail para extraer la cola
}
}
En general, podemos usar pattern matching para evitar tener que validar manualmente si una lista está vacía y para acceder directamente a la cabeza y cola sin llamar a métodos explícitos.
Tener en cuenta algunas operaciones importantes sobre listas:
scala> val l = List(1, 2, 3, 4, 5) // Creación de una lista de enteros
val l: List[Int] = List(1, 2, 3, 4, 5)
scala> l.head // Obtiene el primer elemento de la lista
val res2: Int = 1
scala> l.tail // Obtiene todos los elementos excepto el primero
val res3: List[Int] = List(2, 3, 4, 5)
scala> l.take(3) // Toma los primeros 3 elementos de la lista
val res4: List[Int] = List(1, 2, 3)
scala> l.drop(3) // Elimina los primeros 3 elementos de la lista
val res5: List[Int] = List(4, 5)
scala> l.splitAt(3) // Divide la lista en dos partes en la posición 3
val res6: (List[Int], List[Int]) = (List(1, 2, 3), List(4, 5))
Conceptos teóricos adicionales¶
Estructura recursiva de listas: En Scala, List es una estructura de datos recursiva donde cada lista es o bien Nil (vacía) o bien un elemento (head) seguido de otra lista (tail).
Operador :: (cons): El operador :: construye una nueva lista agregando un elemento al principio de una lista existente. Es asociativo por la derecha: 1 :: 2 :: 3 :: Nil equivale a 1 :: (2 :: (3 :: Nil)).
Pattern matching exhaustivo: Al trabajar con listas, es importante cubrir ambos casos (Nil y x :: xs) para evitar errores en tiempo de ejecución.
Recursión estructural: Muchas operaciones sobre listas se implementan naturalmente mediante recursión estructural, donde el caso base es Nil y el caso recursivo procesa head y llama recursivamente sobre tail.
Inmutabilidad: Las listas en Scala son inmutables por defecto. Operaciones como tail, take, drop y splitAt devuelven nuevas listas sin modificar la original.
Tabla de resumen¶
| Concepto | Descripción | Ejemplo en Scala | Uso en pattern matching |
|---|---|---|---|
| Lista vacía (Nil) | Caso base de la definición recursiva de listas | Nil o List() |
case Nil => ... |
| Cons (::) | Operador para construir listas agregando elemento al inicio | 1 :: List(2, 3) |
case x :: xs => ... |
| Head | Primer elemento de una lista no vacía | list.head |
Extraído como x en x :: xs |
| Tail | Todos los elementos excepto el primero | list.tail |
Extraído como xs en x :: xs |
| Take | Toma los primeros n elementos | list.take(3) |
No aplica directamente |
| Drop | Elimina los primeros n elementos | list.drop(2) |
No aplica directamente |
| SplitAt | Divide la lista en dos partes | list.splitAt(3) |
No aplica directamente |
| Recursión estructural | Patrón de procesamiento recursivo sobre estructura de datos | Procesar head y llamar recursivo sobre tail | Natural con case x :: xs => f(x) :: procesar(xs) |
| Inmutabilidad | Las listas no pueden modificarse después de creadas | Operaciones devuelven nuevas listas | Seguro para pattern matching |
Comentarios adicionales¶
- El pattern matching sobre listas es más seguro que usar
headytaildirectamente, ya que evitaNoSuchElementExceptioncuando la lista está vacía. - El operador
::es un método del objetoListque se puede usar en notación infija debido a que termina con:. - Para listas con más de un elemento en el patrón, se puede usar:
case x :: y :: rest => ...para extraer los dos primeros elementos. - El pattern matching sobre listas es fundamental para algoritmos como map, filter, fold, y otros transformadores de listas.
- La recursión sobre listas usando pattern matching es más eficiente que usar índices, ya que las listas enlazadas no tienen acceso aleatorio eficiente.
- Se pueden anidar patrones para estructuras más complejas:
case (x :: xs) :: yss => ...para listas de listas. - El uso de
@(as-pattern) permite capturar tanto el patrón completo como sus partes:case listaCompleta @ (x :: xs) => ... - Para optimizar la recursión sobre listas grandes, se puede usar recursión de cola (tail recursion) para evitar desbordamiento de pila.