Programación Funcional
Scala es un lenguaje diseñado para soportar completamente la programación funcional, ofreciendo características como funciones de orden superior, inmutabilidad, expresiones lambda y estructuras que facilitan trabajar de manera declarativa.
Principios de la Programación Funcional
- Inmutabilidad: Los datos no cambian después de su creación, lo que evita efectos secundarios.
- Funciones puras: Una función siempre devuelve el mismo resultado para los mismos argumentos, sin modificar el estado global.
- Funciones como ciudadanos de primera clase: Las funciones pueden asignarse a variables, pasarse como argumentos y devolverse como valores.
- Composición de funciones: Permite combinar funciones pequeñas para crear soluciones más complejas.
- Expresiones en lugar de instrucciones: En lugar de cambiar estados, se evalúan expresiones para producir nuevos valores.
Funciones en Scala
Declaración de Funciones
def sumar(a: Int, b: Int): Int = {
a + b
}
println(sumar(3, 5)) // Salida: 8Funciones Lambda (Funciones Anónimas)
Las funciones lambda son definiciones de funciones compactas.
val multiplicar = (a: Int, b: Int) => a * b
println(multiplicar(2, 3)) // Salida: 6Funciones de Orden Superior
Una función de orden superior es una función que acepta otra función como argumento o devuelve una función.
Ejemplo: Aplicar una función a una lista
val numeros = List(1, 2, 3)
val cuadrados = numeros.map(x => x * x)
println(cuadrados) // Salida: List(1, 4, 9)Ejemplo: Devuelve una función
def multiplicador(factor: Int): Int => Int = {
(x: Int) => x * factor
}
val porDos = multiplicador(2)
println(porDos(5)) // Salida: 10Currificación
La currificación convierte una función que toma múltiples argumentos en una secuencia de funciones que toman un único argumento cada una.
def sumar(a: Int)(b: Int): Int = a + b
val sumaParcial = sumar(10)_
println(sumaParcial(5)) // Salida: 15Inmutabilidad
La inmutabilidad garantiza que los datos no cambien después de su creación. Esto se logra mediante el uso de val y estructuras de datos inmutables.
val lista = List(1, 2, 3)
val nuevaLista = lista :+ 4 // Agrega un elemento sin modificar la original
println(nuevaLista) // Salida: List(1, 2, 3, 4)Pattern Matching
El pattern matching es una característica poderosa de Scala que permite analizar estructuras de datos de forma concisa y expresiva.
def clasificarNumero(numero: Int): String = numero match {
case 1 => "Uno"
case 2 => "Dos"
case _ => "Otro número"
}
println(clasificarNumero(2)) // Salida: DosComposición de Funciones
La composición de funciones permite combinar funciones más pequeñas en otras más grandes y complejas.
val sumarUno = (x: Int) => x + 1
val duplicar = (x: Int) => x * 2
val sumarYDuplicar = sumarUno.andThen(duplicar)
println(sumarYDuplicar(3)) // Salida: 8Operaciones Funcionales en Colecciones
map
Transforma cada elemento de una colección aplicando una función.
val numeros = List(1, 2, 3)
val dobles = numeros.map(_ * 2)
println(dobles) // Salida: List(2, 4, 6)filter
Filtra elementos que cumplen una condición.
val pares = numeros.filter(_ % 2 == 0)
println(pares) // Salida: List(2)reduce
Combina todos los elementos de una colección utilizando una operación binaria.
val suma = numeros.reduce(_ + _)
println(suma) // Salida: 6flatMap
Aplica una función a cada elemento de una colección y aplana el resultado.
val palabras = List("Hola", "Scala")
val letras = palabras.flatMap(_.toLowerCase)
println(letras) // Salida: List(h, o, l, a, s, c, a, l, a)Recursividad
La recursividad es un concepto clave en la programación funcional. Scala soporta recursividad de cola, lo que permite optimizaciones en tiempo de ejecución.
def factorial(n: Int): Int = {
if (n == 0) 1
else n * factorial(n - 1)
}
println(factorial(5)) // Salida: 120Buenas Prácticas
- Prefiere funciones puras: Reduce efectos secundarios para facilitar el razonamiento sobre el código.
- Usa inmutabilidad: Evita modificar estructuras de datos existentes.
- Divide y vencerás: Divide funciones grandes en pequeñas y reutilizables.
- Evita bucles imperativos: Usa métodos funcionales como
map,filterofold. - Aprovecha el pattern matching: Simplifica el análisis de datos complejos.
Conclusión
La programación funcional en Scala fomenta un enfoque declarativo, seguro y modular para resolver problemas. Sus herramientas y conceptos, como funciones de orden superior, inmutabilidad y composición, permiten crear soluciones elegantes y escalables.
