Ejemplos reconocimiento
import scala.annotation.tailrec
object Listas {
// SUMA CON IF-ELSE
// Versión imperativa usando isEmpty, head y tail
def suma(l:List[Int]):Int = {
@tailrec
def sumaR(l:List[Int])(acc:Int):Int = {
if (l.isEmpty) acc // Caso base: lista vacía
else sumaR(l.tail)(l.head + acc) // Recursión: cabeza + acumulador
}
sumaR(l)(0) // Inicia con acumulador 0
}
// SUMA CON PATTERN MATCHING
// Versión funcional usando reconocimiento de patrones
def sumaM(l:List[Int]):Int = {
@tailrec
def sumaR(l:List[Int])(acc:Int):Int = {
l match {
case Nil => acc // Patrón: lista vacía
case h :: t => sumaR(t)(acc + h) // Patrón: cabeza + cola
}
}
sumaR(l)(0)
}
// MAPEO CON PATTERN MATCHING
// Aplica función f a cada elemento de la lista
def mapeo(l:List[Int])(f:Int => Int):List[Int] = {
@tailrec
def mapeoR(l:List[Int])(acc:List[Int]):List[Int] = {
l match {
case Nil => acc.reverse // Caso base: reversa para mantener orden
case h :: t => mapeoR(t)(f(h) :: acc) // Aplica f y acumula
}
}
mapeoR(l)(Nil)
}
// FILTRO CON PATTERN MATCHING
// Conserva elementos que cumplen condición f
def filtro(l:List[Int])(f:Int => Boolean):List[Int] = {
@tailrec
def filtroR(l:List[Int])(acc:List[Int]):List[Int] = {
l match {
case Nil => acc.reverse // Caso base
case h :: t =>
if (f(h)) filtroR(t)(h :: acc) // Si cumple, añade al acumulador
else filtroR(t)(acc) // Si no, ignora
}
}
filtroR(l)(Nil)
}
def main(arr:Array[String]):Unit = {
val x = List(1,2,3,4,5)
// Pruebas de suma
println(suma(x)) // 15
println(sumaM(x)) // 15
// Comparación mapeo manual vs built-in
println(mapeo(x)(a => a * 2)) // [2,4,6,8,10] (manual)
println(mapeo(x)(a => a * a)) // [1,4,9,16,25] (manual)
println(x.map(a => a * 2)) // [2,4,6,8,10] (built-in)
println(x.map(a => a * a)) // [1,4,9,16,25] (built-in)
// Comparación filtro manual vs built-in
println(filtro(x)(a => a % 2 == 0)) // [2,4] (manual)
println(x.filter(a => a % 2 == 0)) // [2,4] (built-in)
println(filtro(x)(a => a > 3)) // [4,5] (manual)
println(x.filter(a => a > 3)) // [4,5] (built-in)
}
}
2. RECONOCIMIENTO DE PATRONES PARA LISTAS:
El pattern matching descompone listas en dos patrones fundamentales:
- Nil: Lista vacía (caso base)
- h :: t: Cabeza (primer elemento) y cola (resto de la lista)
Ventajas:
- Sintaxis clara: h :: t es más expresivo que l.head/l.tail
- Seguridad: Evita excepciones con acceso a listas vacías
- Exhaustividad: Compilador verifica que se cubren todos los casos
3. LIMITACIÓN DEL PATTERN MATCHING:
El pattern matching solo reconoce estructuras, no condiciones sobre valores:
// NO ES POSIBLE hacer esto:
l match {
case (h if h % 2 == 0) :: t => ... // Error: no se puede poner condición en patrón de lista
case _ => ...
}
// POR ESO se necesita el if dentro del caso:
case h :: t => if (h % 2 == 0) ... else ...
Limitación: El pattern matching no puede evaluar propiedades específicas de los elementos (como "ser par"), solo puede reconocer la estructura de la lista.
4. INTRODUCCIÓN A MAP Y FILTER:
Map: Transforma cada elemento aplicando una función
- List(1,2,3).map(x => x * 2) → List(2,4,6)
- No cambia la longitud de la lista
- Aplica la misma operación a todos los elementos
Filter: Selecciona elementos que cumplen una condición
- List(1,2,3,4).filter(x => x % 2 == 0) → List(2,4)
- Puede reducir la longitud de la lista
- Conserva el orden original
Características comunes: - Operaciones de orden superior (reciben funciones como parámetros) - No modifican la lista original (inmutabilidad) - Retornan nuevas listas - Fundamentales en programación funcional