Fermetures Scala
En Scala, une fermeture est une fonction qui peut capturer et inclure des variables libres de son environnement de définition.
Une fermeture est une fonction dont la valeur de retour dépend d’une ou plusieurs variables déclarées en dehors de la fonction.
Une fermeture peut accéder à des variables extérieures à la fonction et les utiliser à l’intérieur de la fonction. Même lorsque la portée des variables a dépassé son environnement de définition, la fermeture peut toujours accéder à ces variables.
La clé d’une fermeture est qu’elle peut « se refermer sur » des variables extérieures à la fonction, ce qui rend ces variables encore disponibles à l’intérieur de la fonction.
Comme la fonction anonyme suivante :
val multiplier = (i:Int) => i * 10
À l’intérieur du corps de la fonction se trouve une variable i, qui est un paramètre de la fonction. Comme dans l’extrait de code suivant :
val multiplier = (i:Int) => i * factor
Dans multiplier, il y a deux variables : i et factor. L’une d’elles, i, est un paramètre formel de la fonction ; lorsque la fonction multiplier est appelée, une nouvelle valeur est affectée à i. Cependant, factor n’est pas un paramètre formel mais une variable libre. Considérez le code suivant :
var factor = 3 val multiplier = (i:Int) => i * factor
Ici, nous introduisons une variable libre factor, qui est définie en dehors de la fonction.
La variable de fonction multiplier définie de cette manière devient une « fermeture », car elle référence une variable définie en dehors de la fonction. Le processus de définition de cette fonction capture cette variable libre pour former une fonction fermée.
Exemple complet
Exemple
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
}
L'exécution du code ci-dessus produit la sortie suivante :
$ scalac Test.scala $ scala Test muliplier(1) value = 3 muliplier(2) value = 6
Dans l’exemple suivant, la fonction de fermeture capture la variable externe externalVar. Lorsque la valeur de externalVar change, le résultat du calcul à l’intérieur de la fermeture change en conséquence.
Exemple
def main(args: Array[String]): Unit = {
var externalVar = 10
val closure = (x: Int) => x + externalVar
println(closure(5)) // Sortie 15
externalVar = 20
println(closure(5)) // Sortie 25
}
}
L'exécution du code ci-dessus produit la sortie suivante :
15 25
Les fermetures comme valeurs de retour
Dans l'exemple suivant, la fonction makeAdder renvoie une fermeture. Différentes instances de fermeture capturent différentes valeurs d'adder.
Exemple
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)) // Sortie 8
println(addTen(3)) // Sortie 13
}
}
L'exécution du code ci-dessus produit la sortie suivante :
8 13
Types de variables capturées par les fermetures
Les fermetures peuvent capturer des variables de différents types, y compris les types valeur et les types référence.
Dans l'exemple suivant, la fermeture addMessage capture la variable messages et peut la modifier.
Exemple
def main(args: Array[String]): Unit = {
var messages = List("Hello", "World")
val addMessage = (msg: String) => messages = messages :+ msg
addMessage("Scala")
println(messages) // Sortie List(Hello, World, Scala)
}
}
L'exécution du code ci-dessus produit la sortie suivante :
List(Hello, World, Scala)
Fermetures et programmation fonctionnelle
Les fermetures sont un concept important en programmation fonctionnelle et sont largement utilisées dans des techniques telles que les fonctions d'ordre supérieur et le currying.
Dans l'exemple suivant, la fonction highOrderFunction accepte une fonction comme paramètre. La fermeture passée capture la variable externe externalVar.
Exemple
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)) // Sortie 15
}
}
L'exécution du code ci-dessus produit la sortie suivante :
15Autres extensions