Loading AI tools
astrazione nei linguaggi di programmazione Da Wikipedia, l'enciclopedia libera
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.
Le 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].
Le 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 f
che restituisce il parametro passatogli più 1:
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 f
contiene un riferimento alla variabile y
presente nell'ambiente di definizione, in cui è legata al valore 1.
Poiché in ML il valore di y
non può cambiare il comportamento di f
non cambia.
Invece se si usa una variabile assegnabile, f
restituisce il valore del suo parametro sommato al valore di y
al momento in cui viene chiamata.
Ad esempio:
val y = ref 1;
let f(x) = x + !y;
f(3);
y := 5;
f(3);
La prima chiamata di f 3
restituisce 4, la seconda invece 8.
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 y
, quella del let
più esterno, in cui vale 1, e quella del let
più interno, in cui vale 2.
La chiusura f
viene creata nell'ambiente più esterno, e fa quindi riferimento alla y
con valore 1 e per questo, il risultato che si ottiene è 4, non 5.
Le 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.
Scheme è 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:
In 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:
final
dichiarati nel metodo nel quale è racchiusa.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)
()
(operatore di applicazione della funzione). Questo genere di classi sono chiamate oggetti funzione o, occasionalmente, funtori (anche se quest'ultimo termine è ambiguo, dato che in altri linguaggi ha un significato molto diverso). Questi oggetti funzione si comportano in modo simile alle funzioni nei linguaggi di programmazione funzionali, ma sono diversi dalle chiusure nel fatto che le variabili del loro ambiente non vengono "catturate". Per simulare una vera chiusura, si può immaginare di mettere tutte le variabili globali in una singola struttura (struct
) e di passarne una copia ad un oggetto funzione.delegate
) di memorizzare un riferimento ad un metodo di un'istanza di una classe (cioè un oggetto); quando verrà chiamato questo delegato, il metodo verrà invocato per quella particolare istanza. Per simulare una vera chiusura, si può creare un'istanza di una classe (cioè un oggetto) con gli attributi necessari. Un altro modo per simulare le chiusure è stato introdotto dalla versione 2.0 del linguaggio, con l'utilizzo dei metodi anonimi.Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.