Julia Language
chiusure
Ricerca…
Sintassi
- x -> [corpo]
- (x, y) -> [corpo]
- (xs ...) -> [corpo]
Osservazioni
Nelle versioni precedenti di Julia, le chiusure e le funzioni anonime avevano una penalità legata alle prestazioni in fase di esecuzione. Questa penalità è stata eliminata in 0,5.
Composizione funzionale
Possiamo definire una funzione per eseguire la composizione di funzioni usando la sintassi della funzione anonima :
f ∘ g = x -> f(g(x))
Si noti che questa definizione è equivalente a ciascuna delle seguenti definizioni:
∘(f, g) = x -> f(g(x))
o
function ∘(f, g)
x -> f(g(x))
end
ricordando che in Julia, f ∘ g
è solo lo zucchero di sintassi per ∘(f, g)
.
Possiamo vedere che questa funzione si compone correttamente:
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
Nella versione v0.5, questa definizione è molto performante. Possiamo esaminare il codice LLVM generato:
julia> @code_llvm sextuple(1)
define i64 @"julia_#17_71238"(i64) #0 {
top:
%1 = mul i64 %0, 6
ret i64 %1
}
È chiaro che le due moltiplicazioni sono state piegate in un'unica moltiplicazione e che questa funzione è il più efficiente possibile.
Come funziona questa funzione di ordine superiore? Crea una cosiddetta chiusura , che consiste non solo nel suo codice, ma tiene anche traccia di determinate variabili dal suo ambito. Tutte le funzioni di Julia che non sono state create nell'ambito di livello superiore sono le chiusure.
Si possono ispezionare le variabili chiuse attraverso i campi della chiusura. Ad esempio, vediamo che:
julia> (sin ∘ cos).f
sin (generic function with 10 methods)
julia> (sin ∘ cos).g
cos (generic function with 10 methods)
Implementare Currying
Un'applicazione di chiusure è di applicare parzialmente una funzione; cioè, fornire alcuni argomenti ora e creare una funzione che accetta gli argomenti rimanenti. Il curry è una forma specifica di applicazione parziale.
Iniziamo con la semplice funzione curry(f, x)
che fornirà il primo argomento di una funzione e aspettiamo ulteriori argomenti in seguito. La definizione è abbastanza semplice:
curry(f, x) = (xs...) -> f(x, xs...)
Ancora una volta, usiamo la sintassi della funzione anonima , questa volta in combinazione con la sintassi degli argomenti variadici.
Possiamo implementare alcune funzioni di base in stile tacito (o punto libero) usando questa funzione 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?
Le funzioni mantengono il generismo atteso:
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
Introduzione alle chiusure
Le funzioni sono una parte importante della programmazione di Julia. Possono essere definiti direttamente all'interno dei moduli, nel qual caso le funzioni vengono definite di primo livello . Ma le funzioni possono anche essere definite all'interno di altre funzioni. Tali funzioni sono chiamate " chiusure ".
Le chiusure catturano le variabili nella loro funzione esterna. Una funzione di primo livello può utilizzare solo variabili globali dal proprio modulo, parametri di funzione o variabili locali:
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
Una chiusura, d'altra parte, può utilizzare tutti quelli oltre alle variabili dalle funzioni esterne che cattura:
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
Se eseguiamo c = toplevel(10)
, vediamo che il risultato è
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)
Si noti che l'espressione di coda di questa funzione è una funzione in sé; cioè, una chiusura. Possiamo chiamare la chiusura c
come se fosse un'altra funzione:
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)
Si noti che c
ha ancora accesso alle variabili y
e z
dal toplevel
chiamata - anche se toplevel
è già tornato! Ogni chiusura, anche quelli restituiti dalla stessa funzione, si chiude su diverse variabili. Possiamo chiamare di nuovo 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)
Si noti che nonostante d
e c
abbiano lo stesso codice e che vengano passati gli stessi argomenti, il loro output è diverso. Sono chiusure distinte.