Conceptos de programación funcional¶
- Ligaduras (bindings): Valores inmutables que no pueden modificarse después de su definición.
- Recursión como estrategia de solución: Puede ser lineal, de cola (tail recursion) o en árbol.
- Funciones de alto orden (higher-order functions): Funciones que reciben otras funciones como argumentos o retornan funciones como resultado.
Ejercicio¶
- Desarrollar un programa que, dado un
n, retorne la lista desde0!hastan!. Usar recursión de cola. - Desarrollar un programa que reciba
nymy retorne los números primos en ese rango (donden < m).
import scala.annotation.tailrec
class Ejemplo {
// Función principal que retorna la lista de factoriales desde 0! hasta n!
def listaFactoriales(n:Int):List[Long] = {
// Función auxiliar recursiva de cola que construye la lista
@tailrec
def listaFactorialesAux(n:Int, acc:List[Long]):List[Long] = {
if (n == 0) {
1L :: acc // Caso base: agrega 0! = 1 a la lista acumulada
}
else{
listaFactorialesAux(n-1, factorial(n) :: acc) // Caso recursivo: calcula n! y lo agrega
}
}
// Función para calcular factorial con recursión de cola
@tailrec
def factorial(n:Int, acc:Long = 1L):Long = {
if (n == 0) acc // Caso base: retorna el acumulador
else factorial(n-1, acc * n) // Caso recursivo: multiplica y continúa
}
listaFactorialesAux(n, List()) // Llama a la función auxiliar con lista vacía inicial
}
// Función que retorna los números primos en el rango [n, m]
def primosRango(n:Int, m:Int):List[Int] = {
// Función auxiliar para verificar si un número es primo
def esPrimo(num:Int):Boolean = {
// Verifica divisibilidad desde 2 hasta √num
(2 to Math.ceil(Math.sqrt(num)).toInt) forall (x => num % x != 0)
}
// Filtra el rango [n, m] conservando solo los números primos
(n to m).filter(esPrimo).toList
}
// Método principal para probar las funciones
def main(arr: Array[String]):Unit = {
println(listaFactoriales(6)) // Imprime factoriales de 0 a 6
println(primosRango(2, 100)) // Imprime primos entre 2 y 100
}
}
Conceptos teóricos adicionales¶
- Recursión de cola (tail recursion): Una función es recursiva de cola cuando la llamada recursiva es la última operación que se ejecuta. Scala puede optimizarla para evitar crecimiento de la pila (stack overflow).
- Inmutabilidad en Scala: Aunque Scala permite mutabilidad, el estilo funcional favorece valores inmutables usando
valen lugar devar. - Funciones de orden superior en Scala:
map,filter,fold,forallson ejemplos comunes que operan sobre colecciones. - Anotación
@tailrec: Garantiza en tiempo de compilación que la función es recursiva de cola; si no lo es, produce un error. - Rangos en Scala:
(n to m)crea un rango inclusivo;(n until m)crea un rango exclusivo del límite superior.
Tabla de resumen¶
| Concepto | Descripción | Ejemplo en Scala |
|---|---|---|
| Ligaduras inmutables | Valores que no cambian después de definirse. | val x = 5 |
| Recursión lineal | Función que se llama a sí misma una vez por ejecución. | Función factorial clásica |
| Recursión de cola | Recursión donde la llamada recursiva es la última operación. | factorial con acumulador |
Anotación @tailrec |
Asegura optimización de recursión de cola. | @tailrec def f(...) |
| Funciones de alto orden | Funciones que operan sobre otras funciones. | filter, map, forall |
| Funciones anidadas | Funciones definidas dentro de otras funciones. | listaFactorialesAux dentro de listaFactoriales |
| Rangos | Secuencias de números enteros. | (1 to 10), (1 until 10) |
Método forall |
Verifica que todos los elementos cumplan una condición. | range.forall(predicate) |
| Inmutabilidad de listas | Las listas en Scala son inmutables por defecto. | List(1, 2, 3) |
Operador :: |
Agrega un elemento al inicio de una lista (cons). | 1 :: List(2, 3) |
Comentarios adicionales¶
- En programación funcional, la inmutabilidad es clave: evita efectos secundarios y hace el código más predecible.
- La recursión de cola es esencial para procesar grandes conjuntos de datos sin desbordar la pila.
- Scala es un lenguaje multiparadigma que combina programación funcional y orientada a objetos.
- El uso de funciones de orden superior (
filter,map, etc.) produce código más declarativo y conciso. - Para mejorar el rendimiento en la verificación de primos, se puede limitar la prueba de divisibilidad hasta la raíz cuadrada del número.
- En Scala, es buena práctica usar
valpara valores inmutables y reservarvarpara casos excepcionales donde se necesite mutabilidad. - La anotación
@tailreces una herramienta de seguridad que ayuda a prevenir errores de desbordamiento de pila en funciones recursivas.