Programacion Funcional
## Objetivos de Aprendizaje
- Comprender principios de programacion funcional
- Dominar funciones de orden superior
- Aprender closures
- Trabajar con currying
- Comprender aplicacion parcial
## Principios de Programacion Funcional
### Funciones Puras
Una funcion pura:
- Devuelve el mismo resultado para las mismas entradas
- No tiene efectos secundarios (sin E/S, sin mutacion)
```scala
// Funcion pura
def add(a: Int, b: Int): Int = a + b
// Funcion impura (tiene efecto secundario)
var counter = 0
def nextInt(): Int = {
counter += 1
counter
}
```
### Inmutabilidad
Prefiere estructuras de datos inmutables:
```scala
// Enfoque inmutable
val original = List(1, 2, 3)
val added = 0 :: original // Nueva lista: List(0, 1, 2, 3)
// Enfoque mutable (evitar cuando sea posible)
import scala.collection.mutable
val buffer = mutable.ListBuffer(1, 2, 3)
buffer.prepend(0) // Muta buffer
```
## Funciones de Orden Superior
Funciones que toman funciones como parametros o devuelven funciones:
```scala
def applyTwice(f: Int => Int, x: Int): Int = f(f(x))
def double(x: Int): Int = x * 2
def increment(x: Int): Int = x + 1
applyTwice(double, 3) // 12 (3 * 2 * 2)
applyTwice(increment, 3) // 5 (3 + 1 + 1)
```
### Funciones de Orden Superior Comunes
```scala
val numbers = List(1, 2, 3, 4, 5)
// map - transforma cada elemento
numbers.map(_ * 2) // List(2, 4, 6, 8, 10)
// filter - mantiene elementos que satisfacen el predicado
numbers.filter(_ % 2 == 0) // List(2, 4)
// flatMap - map y luego flatten
numbers.flatMap(x => List(x, -x)) // List(1, -1, 2, -2, 3, -3, 4, -4, 5, -5)
// fold/reduce - combina elementos
numbers.fold(0)(_ + _) // 15 (suma)
numbers.reduce(_ * _) // 120 (producto)
// collect - emparejamiento de patrones y transformacion
numbers.collect {
case x if x % 2 == 0 => x * 2
} // List(4, 8)
```
## Closures
Un closure captura variables de su entorno:
```scala
def multiplier(factor: Int): Int => Int = {
(x: Int) => x * factor // factor es capturado
}
val triple = multiplier(3)
triple(5) // 15
val double = multiplier(2)
double(5) // 10
```
### Captura Mutable
```scala
var factor = 1
val incrementAndMultiply: Int => Int = (x: Int) => {
factor += 1
x * factor
}
incrementAndMultiply(5) // 6 (factor se convierte en 2)
incrementAndMultiply(5) // 15 (factor se convierte en 3)
```
## Currying
Convertir una funcion con multiples parametros en una cadena de funciones:
```scala
def add(a: Int)(b: Int): Int = a + b
add(3)(5) // 8
val addFive = add(5) _
addFive(3) // 8
addFive(10) // 15
```
### Beneficios del Currying
```scala
// Sin currying
def withLogging(f: () => Unit): () => Unit = {
() => {
println("Before")
f()
println("After")
}
}
// Con currying
def withLogging(f: => Unit): Unit = {
println("Before")
f
println("After")
}
def greet(): Unit = println("Hello!")
withLogging(greet)
```
## Aplicacion Parcial
Proporcionar algunos argumentos, dejando otros para despues:
```scala
def multiply(a: Int, b: Int, c: Int): Int = a * b * c
val multiplyBy2 = multiply(2, _, 3) // Parcialmente aplicada
multiplyBy2(5) // 30 (2 * 5 * 3)
val multiplyBy6 = multiply(2, 3, _) // Parcialmente aplicada
multiplyBy6(5) // 30 (2 * 3 * 5)
```
## Composicion de Funciones
### Compose
```scala
val double = (x: Int) => x * 2
val addOne = (x: Int) => x + 1
val doubleAddOne = double compose addOne // (x + 1) * 2
doubleAddOne(5) // 12
```
### AndThen
```scala
val double = (x: Int) => x * 2
val addOne = (x: Int) => x + 1
val doubleThenAddOne = double andThen addOne // (x * 2) + 1
doubleThenAddOne(5) // 11
```
### Usando con expresiones for
```scala
case class Person(name: String, age: Int, city: String)
val people = List(
Person("Alice", 30, "NYC"),
Person("Bob", 25, "LA"),
Person("Charlie", 35, "NYC")
)
val nycNames = for {
p <- people
if p.city == "NYC"
} yield p.name
// List("Alice", "Charlie")
```
## Estructuras de Datos Funcionales Puras
### Listas Persistentes
```scala
sealed trait List[+A]
case object Nil extends List[Nothing]
case class Cons[A](head: A, tail: List[A]) extends List[A]
object List {
def apply[A](as: A*): List[A] =
if (as.isEmpty) Nil
else Cons(as.head, apply(as.tail: _*))
def foldRight[A, B](as: List[A], z: B)(f: (A, B) => B): B =
as match {
case Nil => z
case Cons(x, xs) => f(x, foldRight(xs, z)(f))
}
}
```
## Evaluacion Perezosa
### Evaluacion Perezosa con `lazy`
```scala
lazy val expensive = {
println("Computing...")
42
}
println("Before")
println(expensive) // Computing... luego 42
println(expensive) // Solo 42
```
### Listas Perezosas (Streams)
```scala
import scala.collection.immutable.LazyList
def fibonacci: LazyList[Int] = {
def go(a: Int, b: Int): LazyList[Int] = a #:: go(b, a + b)
go(0, 1)
}
fibonacci.take(10).toList // List(0, 1, 1, 2, 3, 5, 8, 13, 21, 34)
```
## Tipos de Funciones
| Tipo | Descripcion |
|------|-------------|
| `Int => Int` | Funcion de Int a Int |
| `(Int, Int) => Int` | Funcion con dos parametros Int |
| `() => Unit` | Funcion sin parametros |
| `Int => Boolean` | Predicado sobre Int |
| `(Int, Int) => Boolean` | Predicado binario |
## Metodos vs Funciones
```scala
class Calculator {
def add(a: Int, b: Int): Int = a + b
}
val calc = new Calculator()
// Metodo - eta expansion convierte a funcion
val addFunction: (Int, Int) => Int = calc.add _
// Funcion directa
val add = (a: Int, b: Int) => a + b
```
## Optimizacion de Recursion de Cola
```scala
def factorial(n: Int): BigInt = {
@annotation.tailrec
def loop(acc: BigInt, n: Int): BigInt = {
if (n <= 1) acc
else loop(acc * n, n - 1)
}
loop(1, n)
}
```
## Resumen
- La programacion funcional enfatiza funciones puras e inmutabilidad
- Las funciones de orden superior toman o devuelven funciones
- Los closures capturan variables del entorno
- El currying divide funciones de multiples parametros en cadenas
- La aplicacion parcial fixa algunos argumentos para despues
- La composicion de funciones crea nuevas funciones a partir de existentes
- La evaluacion perezosa difiere el calculo hasta que sea necesario
- La recursion de cola se optimiza para evitar desbordamiento de pila
- Preferir val sobre var, inmutable sobre mutable
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →