Julia Language
Fermetures
Recherche…
Syntaxe
- x -> [body]
- (x, y) -> [body]
- (xs ...) -> [body]
Remarques
Dans les anciennes versions de Julia, les fermetures et les fonctions anonymes entraînaient une pénalité d'exécution. Cette pénalité a été éliminée en 0.5.
Composition de la fonction
Nous pouvons définir une fonction pour effectuer une composition de fonctions en utilisant une syntaxe de fonction anonyme :
f ∘ g = x -> f(g(x))
Notez que cette définition est équivalente à chacune des définitions suivantes:
∘(f, g) = x -> f(g(x))
ou
function ∘(f, g)
x -> f(g(x))
end
rappelant que dans Julia, f ∘ g
est juste du sucre syntaxique pour ∘(f, g)
.
Nous pouvons voir que cette fonction compose correctement:
julia> double(x) = 2x
double (generic function with 1 method)
julia> triple(x) = 3x
triple (generic function with 1 method)
julia> const sextuple = double ∘ triple
(::#17) (generic function with 1 method)
julia> sextuple(1.5)
9.0
Dans la version v0.5, cette définition est très performante. Nous pouvons examiner le code LLVM généré:
julia> @code_llvm sextuple(1)
define i64 @"julia_#17_71238"(i64) #0 {
top:
%1 = mul i64 %0, 6
ret i64 %1
}
Il est clair que les deux multiplications ont été pliées en une seule multiplication et que cette fonction est aussi efficace que possible.
Comment fonctionne cette fonction d'ordre supérieur? Il crée une soi-disant fermeture , qui consiste non seulement en son code, mais permet également de suivre certaines variables de son étendue. Toutes les fonctions de Julia qui ne sont pas créées au niveau supérieur sont des fermetures.
On peut inspecter les variables fermées dans les champs de la fermeture. Par exemple, nous voyons que:
julia> (sin ∘ cos).f
sin (generic function with 10 methods)
julia> (sin ∘ cos).g
cos (generic function with 10 methods)
Mise en œuvre du curry
Une des applications des fermetures consiste à appliquer partiellement une fonction; c'est-à-dire fournir des arguments maintenant et créer une fonction qui prend les arguments restants. Le curry est une forme spécifique d'application partielle.
Commençons par la fonction simple curry(f, x)
qui fournira le premier argument à une fonction et attendra des arguments supplémentaires plus tard. La définition est assez simple:
curry(f, x) = (xs...) -> f(x, xs...)
Encore une fois, nous utilisons une syntaxe de fonction anonyme , cette fois en combinaison avec la syntaxe des arguments variadiques.
Nous pouvons implémenter certaines fonctions de base dans un style tacite (ou sans point) en utilisant cette fonction curry
.
julia> const double = curry(*, 2)
(::#19) (generic function with 1 method)
julia> double(10)
20
julia> const simon_says = curry(println, "Simon: ")
(::#19) (generic function with 1 method)
julia> simon_says("How are you?")
Simon: How are you?
Les fonctions maintiennent le générisme attendu:
julia> simon_says("I have ", 3, " arguments.")
Simon: I have 3 arguments.
julia> double([1, 2, 3])
3-element Array{Int64,1}:
2
4
6
Introduction aux fermetures
Les fonctions sont une partie importante de la programmation de Julia. Ils peuvent être définis directement dans les modules, auquel cas les fonctions sont appelées niveau supérieur . Mais les fonctions peuvent également être définies dans d'autres fonctions. Ces fonctions sont appelées " fermetures ".
Les fermetures capturent les variables dans leur fonction externe. Une fonction de niveau supérieur ne peut utiliser que les variables globales de leur module, paramètres de fonction ou variables locales:
x = 0 # global
function toplevel(y)
println("x = ", x, " is a global variable")
println("y = ", y, " is a parameter")
z = 2
println("z = ", z, " is a local variable")
end
Une fermeture, par contre, peut utiliser tous ceux en plus des variables des fonctions externes qu'elle capture:
x = 0 # global
function toplevel(y)
println("x = ", x, " is a global variable")
println("y = ", y, " is a parameter")
z = 2
println("z = ", z, " is a local variable")
function closure(v)
println("v = ", v, " is a parameter")
w = 3
println("w = ", w, " is a local variable")
println("x = ", x, " is a global variable")
println("y = ", y, " is a closed variable (a parameter of the outer function)")
println("z = ", z, " is a closed variable (a local of the outer function)")
end
end
Si on lance c = toplevel(10)
, on voit que le résultat est
julia> c = toplevel(10)
x = 0 is a global variable
y = 10 is a parameter
z = 2 is a local variable
(::closure) (generic function with 1 method)
Notez que l'expression en queue de cette fonction est une fonction en soi; c'est une fermeture. On peut appeler la fermeture c
comme s'il s'agissait d'une autre fonction:
julia> c(11)
v = 11 is a parameter
w = 3 is a local variable
x = 0 is a global variable
y = 10 is a closed variable (a parameter of the outer function)
z = 2 is a closed variable (a local of the outer function)
Notez que c
toujours accès aux variables y
et z
de l'appel toplevel
- même si toplevel
a déjà été renvoyé! Chaque fermeture, même celles renvoyées par la même fonction, se ferme sur différentes variables. Nous pouvons appeler à nouveau le toplevel
julia> d = toplevel(20)
x = 0 is a global variable
y = 20 is a parameter
z = 2 is a local variable
(::closure) (generic function with 1 method)
julia> d(22)
v = 22 is a parameter
w = 3 is a local variable
x = 0 is a global variable
y = 20 is a closed variable (a parameter of the outer function)
z = 2 is a closed variable (a local of the outer function)
julia> c(22)
v = 22 is a parameter
w = 3 is a local variable
x = 0 is a global variable
y = 10 is a closed variable (a parameter of the outer function)
z = 2 is a closed variable (a local of the outer function)
Notez que, bien que d
et c
aient le même code, et étant passés les mêmes arguments, leur sortie est différente. Ce sont des fermetures distinctes.