Cierres en Scala
En Scala, un cierre (closure) es una función que puede capturar e incluir variables libres de su entorno de definición.
Un cierre es una función cuyo valor de retorno depende de una o más variables declaradas fuera de la función.
Un cierre puede acceder a variables externas a la función y usarlas dentro de la función, incluso cuando el ámbito de las variables ha sobrepasado su entorno de definición, el cierre aún puede acceder a ellas.
La clave del cierre es que puede «cerrar» variables externas a la función, haciendo que estas variables sigan disponibles dentro de la función.
Como en la siguiente función anónima:
val multiplier = (i:Int) => i * 10
Dentro del cuerpo de la función hay una variable i, que actúa como parámetro de la función. Como en el siguiente fragmento de código:
val multiplier = (i:Int) => i * factor
En multiplier hay dos variables: i y factor. Una de ellas, i, es un parámetro formal de la función; cuando se invoca la función multiplier, a i se le asigna un nuevo valor. Sin embargo, factor no es un parámetro formal, sino una variable libre. Considera el siguiente código:
var factor = 3 val multiplier = (i:Int) => i * factor
Aquí introducimos una variable libre factor, que está definida fuera de la función.
La variable de función multiplier así definida se convierte en un «cierre», porque hace referencia a una variable definida fuera de la función. El proceso de definir esta función consiste en capturar esta variable libre para formar una función cerrada.
Ejemplo completo
Ejemplo
def main(args: Array[String]) {
println( "muliplier(1) value = " + multiplier(1) )
println( "muliplier(2) value = " + multiplier(2) )
}
var factor = 3
val multiplier = (i:Int) => i * factor
}
Al ejecutar el código anterior, el resultado es:
$ scalac Test.scala $ scala Test muliplier(1) value = 3 muliplier(2) value = 6
En el siguiente ejemplo, la función closure captura la variable externa externalVar. Cuando el valor de externalVar cambia, el resultado del cálculo dentro del cierre también cambia en consecuencia.
Ejemplo
def main(args: Array[String]): Unit = {
var externalVar = 10
val closure = (x: Int) => x + externalVar
println(closure(5)) // Salida 15
externalVar = 20
println(closure(5)) // Salida 25
}
}
Al ejecutar el código anterior, el resultado es:
15 25
El cierre como valor de retorno
En el siguiente ejemplo, la función makeAdder devuelve un cierre. Diferentes instancias de cierres capturan diferentes valores de adder.
Ejemplo
def main(args: Array[String]): Unit = {
def makeAdder(adder: Int): Int => Int = {
(x: Int) => x + adder
}
val addFive = makeAdder(5)
val addTen = makeAdder(10)
println(addFive(3)) // Salida 8
println(addTen(3)) // Salida 13
}
}
Al ejecutar el código anterior, el resultado es:
8 13
El tipo de variables capturadas por el cierre
Los cierres pueden capturar variables de diferentes tipos, incluidos tipos por valor y tipos por referencia.
En el siguiente ejemplo, el cierre addMessage captura la variable messages y puede modificarla.
Ejemplo
def main(args: Array[String]): Unit = {
var messages = List("Hello", "World")
val addMessage = (msg: String) => messages = messages :+ msg
addMessage("Scala")
println(messages) // Salida List(Hello, World, Scala)
}
}
Al ejecutar el código anterior, el resultado es:
List(Hello, World, Scala)
Cierres y programación funcional
El cierre es un concepto importante en la programación funcional y se utiliza ampliamente en funciones de orden superior, currificación y otras técnicas.
En el siguiente ejemplo, la función highOrderFunction acepta una función como parámetro. El cierre pasado captura la variable externa externalVar.
Ejemplo
def main(args: Array[String]): Unit = {
def highOrderFunction(f: Int => Int, x: Int): Int = f(x)
val externalVar = 5
val closure = (x: Int) => x + externalVar
println(highOrderFunction(closure, 10)) // Salida 15
}
}
Al ejecutar el código anterior, el resultado es:
15Otras extensiones