Expresiones For¶
Las expresiones for permiten agrupar las operaciones map, flatMap y filter en un solo bloque que es más fácil de comprender y leer. Estas expresiones, conocidas como "for comprehensions", proporcionan una sintaxis declarativa para trabajar con colecciones y otros tipos monádicos.
// Sintaxis general de una expresión for
for{
i <- generador1 // Generador: similar a flatMap
j <- generador2 // Otro generador
...
if condicion // Filtro: similar a filter
if condicion // Puede haber múltiples filtros
} yield ... // Transformación: similar a map
Los generadores (<-) corresponden inicialmente a operaciones flatMap, las condiciones de filtrado con if corresponden a operaciones filter, y finalmente yield actúa como map (genera la salida). Es importante tener cuidado porque la expresión nos produce un IndexedSeq que puede ser necesario convertir al tipo de colección deseado.
Resolución del problema anterior usando for comprehension¶
// Definimos la función para verificar si un número es primo
scala> val esPrimo = ((n:Int) => (2 until n).forall(d => n % d != 0))
val esPrimo: Int => Boolean = $Lambda$1303/0x00007ffb1c5d1220@948335d
// Solución original usando flatMap, map y filter
scala> (1 to 10).flatMap(i => (1 to i).map(j => (i,j))).filter(x => esPrimo(x._1+x._2))
val res38: IndexedSeq[(Int, Int)] = Vector((1,1), (2,1), (3,2), (4,1), (4,3), (5,2), (6,1), (6,5), (7,4), (7,6), (8,3), (8,5), (9,2), (9,4), (9,8), (10,1), (10,3), (10,7), (10,9))
// Solución equivalente usando for comprehension (más legible)
scala> for{
| i <- (1 to 10) // Generador para i de 1 a 10
| j <- (1 to i) // Generador para j de 1 a i (depende de i)
| if esPrimo(i+j) // Filtro: solo pares cuya suma sea primo
| } yield (i,j) // Transformación: produce la tupla (i,j)
val res39: IndexedSeq[(Int, Int)] = Vector((1,1), (2,1), (3,2), (4,1), (4,3), (5,2), (6,1), (6,5), (7,4), (7,6), (8,3), (8,5), (9,2), (9,4), (9,8), (10,1), (10,3), (10,7), (10,9))
// Convertir el resultado a Vector explícitamente
scala> (for{
| i <- (1 to 10)
| j <- (1 to i)
| if esPrimo(i+j)
| } yield (i,j)).toVector
val res40: scala.collection.immutable.Vector[(Int, Int)] = Vector((1,1), (2,1), (3,2), (4,1), (4,3), (5,2), (6,1), (6,5), (7,4), (7,6), (8,3), (8,5), (9,2), (9,4), (9,8), (10,1), (10,3), (10,7), (10,9))
Ejemplo del producto punto¶
// Definición de dos arrays
scala> val x: Array[Int] = Array(1, 2, 3)
val x: Array[Int] = Array(1, 2, 3)
scala> val y = Array(2,3,4)
val y: Array[Int] = Array(2, 3, 4)
// Combinar los arrays en pares usando zip
scala> x zip y
val res41: Array[(Int, Int)] = Array((1,2), (2,3), (3,4))
// Multiplicar los elementos de cada par usando pattern matching
scala> x zip y map ({case (a,b) => a*b})
val res42: Array[Int] = Array(2, 6, 12)
// Calcular la suma de los productos (producto punto)
scala> (x zip y map ({case (a,b) => a*b})).sum
val res45: Int = 20
Conjuntos (Sets)¶
Un conjunto (Set) es una colección desordenada de elementos únicos que no se repiten. Los conjuntos en Scala son inmutables por defecto y proporcionan operaciones matemáticas de conjuntos.
// Creación de un conjunto: los elementos duplicados se eliminan automáticamente
scala> val s1 = Set(1,2,2,2,3,2,2,3)
val s1: scala.collection.immutable.Set[Int] = Set(1, 2, 3)
// Verificar si un elemento está en el conjunto
scala> s1 contains 3
val res46: Boolean = true
scala> s1 contains 10
val res47: Boolean = false
// Creación de otro conjunto
scala> val s2 = Set(2,4,6)
val s2: scala.collection.immutable.Set[Int] = Set(2, 4, 6)
// Unión de conjuntos (todos los elementos de ambos conjuntos)
scala> s1 union s2
val res48: scala.collection.immutable.Set[Int] = HashSet(1, 6, 2, 3, 4)
// Diferencia de conjuntos (elementos en s1 pero no en s2)
scala> s1 diff s2
val res51: scala.collection.immutable.Set[Int] = Set(1, 3)
Como se puede observar, aplicamos las mismas operaciones que en la lógica de conjuntos matemáticos, incluyendo unión, intersección (no mostrada pero disponible como intersect), diferencia y verificación de pertenencia.
Tabla de Resumen¶
| Concepto | Operación/Expresión | Descripción | Equivalencia Funcional | Uso Típico |
|---|---|---|---|---|
| For Comprehension | for { gen... if... } yield expr |
Sintaxis declarativa para operaciones con colecciones | flatMap + filter + map |
Mejorar legibilidad de operaciones anidadas |
Generador (<-) |
i <- coleccion |
Extrae elementos de una colección en un for comprehension | flatMap |
Iterar sobre elementos de colecciones |
Filtro (if) |
if condicion |
Filtra elementos que cumplen una condición | filter |
Seleccionar elementos específicos |
Transformación (yield) |
yield expresion |
Transforma cada elemento en un nuevo valor | map |
Producir resultados transformados |
| Conjunto (Set) | Set(elem1, elem2, ...) |
Colección de elementos únicos sin orden | Estructura matemática de conjuntos | Cuando se necesitan elementos únicos |
Operación zip |
coll1 zip coll2 |
Combina dos colecciones en pares | Combinación elemento a elemento | Operaciones entre colecciones paralelas |
Operación union |
set1 union set2 |
Unión de conjuntos (todos los elementos) | ∪ en matemáticas |
Combinar conjuntos sin duplicados |
Operación diff |
set1 diff set2 |
Diferencia de conjuntos (elementos en set1 pero no en set2) | \ en matemáticas |
Encontrar elementos exclusivos |
Comentarios Adicionales¶
-
Traducción de for comprehensions: Internamente, Scala traduce las expresiones
fora llamadas aflatMap,filterymap. Por ejemplo,for(i <- c1; j <- c2) yield (i,j)se traduce ac1.flatMap(i => c2.map(j => (i,j))). -
For sin yield: Cuando se usa
forsinyield, se ejecutan efectos secundarios (similar aforeach). Esto es útil para iterar sobre colecciones para realizar acciones en lugar de transformarlas. -
Múltiples generadores: Los generadores anidados en expresiones
forproducen el producto cartesiano de las colecciones, similar a bucles anidados. -
Sets y rendimiento: Los conjuntos en Scala tienen generalmente complejidad O(1) para operaciones de búsqueda (
contains), gracias a que internamente usan tablas hash (HashSet) o árboles balanceados (TreeSet para conjuntos ordenados). -
Inmutabilidad por defecto: Los conjuntos creados con
Set()son inmutables. Para conjuntos mutables se debe usarscala.collection.mutable.Set. -
Operaciones de conjuntos: Además de
unionydiff, los conjuntos soportanintersect(intersección),&(intersección),|(unión),&~(diferencia), ysubsetOf(verificación de subconjunto). -
Conversión entre tipos: Los resultados de for comprehensions pueden convertirse a diferentes tipos de colecciones usando métodos como
.toList,.toVector,.toSet, etc., según sea necesario.