Chiusura (informatica)Nei linguaggi di programmazione, una chiusura è una astrazione che combina una funzione con le variabili libere presenti nell'ambiente in cui è definita secondo le regole di scope del linguaggio. Le variabili libere dell'ambiente rimangono accessibili per tutta la durata di vita (extent) della chiusura e pertanto persistono nel corso di invocazioni successive della chiusura. Di conseguenza, le variabili della chiusura possono essere usate per mantenere uno stato ed emulare costrutti della programmazione a oggetti. Le chiusure si rivelano utili quando una funzione ha bisogno di "ricordare" informazioni: ad esempio un parametro specifico per un'operazione di confronto, oppure il riferimento ad un widget in un callback di un'interfaccia grafica. Teoria e implementazioneLe chiusure, tipicamente, sono implementate con una speciale struttura dati che contiene un puntatore al codice della funzione ed una rappresentazione dell'ambiente lessicale della stessa al momento della sua creazione (per esempio l'insieme delle variabili disponibili e dei relativi valori). L'implementazione delle variabili libere legate in una chiusura richiede un trattamento diverso dalle normali variabili che molti linguaggi mantengono su uno stack lineare. Infatti lo stack viene liberato quando si ritorna da una invocazione, mentre le variabili libere di una chiusura devono sopravvivere. Pertanto tali variabili devono essere allocate diversamente in modo da persistere finché non siano più utilizzabili. Di solito quindi le variabili della chiusura sono allocate nello heap e si fa ricorso alla garbage collection per deallocare la chiusura. Questo spiega perché le chiusure sono tipicamente presenti in linguaggi dotati di garbage collector. Il concetto di chiusura può essere collegato a quello di attori del modello ad attori nella computazione concorrente, dove i valori delle variabili nell'ambiente lessicale della funzione sono chiamati conoscenti dell'attore. Un'importante questione sulle chiusure nei linguaggi a paradigma concorrente è se le variabili di una chiusura possono essere aggiornate e, in tal caso, come possono essere sincronizzati questi cambiamenti. Gli attori sono una soluzione a questo problema[1]. Chiusure e funzioniLe chiusure sono presenti in quei linguaggi che trattano le funzioni come oggetti di prima classe (first-class object), cioè consentono di passarle come parametri ad altre funzioni, restituirle come valori da altre funzioni, assegnarle a variabili, ecc., come si fa con i tipi più semplici, quali stringhe, interi, ecc.. In ML, il seguente codice definisce una funzione fun f(x) = x + 1;
Se una funzione utilizza variabili libere, ossia non locali, al momento della sua definizione si crea una chiusura che cattura tali variabili. Per esempio, in questo frammento di codice: val y = 1;
fun f(x) = x + y;
la struttura dati chiusura che rappresenta val y = ref 1;
let f(x) = x + !y;
f(3);
y := 5;
f(3);
La prima chiamata di Per chiarire il ruolo dell'ambiente lessicale, si consideri il seguente frammento in ML: let
val y = 1;
fun f(x) = x + y;
in
let
val y = 2;
in
f(3)
end
end
in questo esempio esistono due diverse variabili con lo stesso nome Utilizzi delle chiusureLe chiusure hanno molti utilizzi:
Nota: a volte vengono definite chiusure anche le strutture dati che combinano un ambiente lessicale con entità che non sono funzioni, ma comunque il termine solitamente si riferisce specificamente alle funzioni. Linguaggi di programmazione con le chiusureScheme è stato il primo linguaggio di programmazione ad avere delle chiusure pienamente generali e con scoping lessicale. Virtualmente, tutti i linguaggi di programmazione funzionali e tutti i linguaggi di programmazione orientati agli oggetti derivati dallo Smalltalk supportano qualche forma di chiusura. Alcuni tra i maggiori linguaggi di programmazione a supportare le chiusure sono:
Simulare le chiusureIn C è possibile usare la parola chiave "static" prima della dichiarazione di una variabile locale. Una variabile static conserva il suo valore attraverso le varie chiamate alla funzione. Questo vale anche per il C++. int Function(int arg)
{
static int numDiChiamate = 0;
static struct Stato infoSulloStato;
return ++numDiChiamate;
}
Alcuni linguaggi orientati agli oggetti permettono al programmatore di usare oggetti che simulano alcune caratteristiche delle chiusure. Per esempio:
public interface Function<Da,A>
{
A apply(Da unParametro);
}
// altro...
public final Function<Integer,Integer> creaSommatore(final int x)
{
return new Function<Integer,Integer>()
{
public Integer apply(Integer unParametro)
{
return x + unParametro;
}
};
}
// altro...
(Si noti che questo esempio usa degli aspetti del linguaggio disponibili solo a partire dalla versione 1.5/5.0 di Java, come i generics, l'autoboxing e l'autounboxing)
NoteBibliografia
Voci correlateCollegamenti esterni
|