Uzávěr (programování)Uzávěr, lexikální uzávěr nebo funkční uzávěr je technika implementace vazby jmen s lexikálním oborem platnosti[pozn. 1] v programovacích jazycích, v nichž jsou funkce objekty první kategorie. Z operačního hlediska je uzávěr záznam, který obsahuje nejen funkci,[pozn. 2] ale také prostředí, ve kterém byla funkce definována.[1] Prostředí zobrazuje každou volnou proměnnou funkce (proměnné, které se ve funkci používají, ale jsou definovány v obklopujícím oboru platnosti) na hodnotu nebo referenci, na kterou bylo jméno vázané v okamžiku, kdy byl uzávěr vytvořen.[pozn. 3] Na rozdíl od běžné funkce uzávěr umožňuje funkci přistupovat k zachyceným proměnným prostřednictvím kopií jejich hodnot nebo referencí vytvořených pro uzávěr, když se uzávěr vyvolá mimo obor jejich platnosti. Historie a etymologieKoncept uzávěrů byl vyvinut v 60. letech 20. století pro mechanické vyhodnocování výrazů v lambda kalkulu a poprvé byl plně implementován v roce 1970 v programovacím jazyce PAL pro podporu funkcí první kategorie s lexikálním oborem platnosti.[2] Termín uzávěr definoval v roce 1964 Peter Landin jako entitu, která má složku prostředí a složku řízení, kterou používal jeho SECD stroj pro vyhodnocování výrazů.[3] Joel Moses uvádí, že termín uzávěr zavedl Landin pro označení anonymní funkce (lambda výrazu) s otevřenými vazbami (volnými proměnnými), které byly uzavřeny (nebo vázány) lexikálním prostředím, čímž vznikl uzavřený výraz neboli uzávěr.[4][5] Toto použití následně převzali Gerald Jay Sussman a Guy L. Steele Jr., když v roce 1975 definovali jazyk Scheme,[6] variantu Lispu s lexikálním oborem platnosti, a následně se rozšířilo. Termín „uzávěr“ použil Sussman s Abelsonem v 80. letech 20. století také v jiném významu: jako vlastnost operátoru, který přidává data k datové struktuře, a je také schopen přidávat vnořené datové struktury. Toto použití termínu „uzávěr“ je převzato z matematiky, zatímco předchozí použití z matematické informatiky. Autoři později označili toto překrývání v terminologii za nešťastné.[7] Anonymní funkce Podrobnější informace naleznete v článku Anonymní funkce.
Termín uzávěr se často používá jako synonymum pro anonymní funkce, i když přísně řečeno, anonymní funkce je funkční literál bez jména, zatímco uzávěr je instance (hodnota) funkce, jejíž nelokální proměnné jsou vázány buď k hodnotám nebo paměťovým lokacím (podle jazyka – viz část lexikální prostředí níže). Například následující program v Pythonu: def f(x):
def g(y):
return x + y
return g # Vrací uzávěr.
def h(x):
return lambda y: x + y # Vrací uzávěr.
# Přiřazení specifického uzávěru do proměnných:
a = f(1)
b = h(1)
# Použití uzávěrů z proměnných:
assert a(5) == 6
assert b(5) == 6
# Použití uzávěrů bez jejich přiřazení do proměnných:
assert f(1)(5) == 6 # f(1) je uzávěr.
assert h(1)(5) == 6 # h(1) je uzávěr.
hodnoty Uzávěr je hodnota jako každá jiná. Nemusí být přiřazena do proměnné, ale může být použita přímo, jak je vidět na posledních dvou řádcích příkladu. Toto použití může být považováno za „anonymní uzávěr“. Vnořené definice funkcí samy o sobě nejsou uzávěry: mají volnou proměnnou, která ještě není vázaná. Teprve po vyhodnocení obklopující funkce s hodnotou parametru je volná proměnná vnořené funkce vázána, čímž vzniká uzávěr, který je pak vrácen z obklopující funkce. Konečně se uzávěr od funkce s volnými proměnnými liší pouze tehdy, když je mimo obor platnosti nelokálních proměnných, jinak se definiční prostředí a prostředí provádění shodují a nelze je ničím rozlišit (statickou a dynamickou vazbu nelze rozlišit, protože jména se resolvují na stejné hodnoty). Například v níže uvedeném programu se funkce s volnou proměnnou x = 1
nums = [1, 2, 3]
def f(y):
return x + y
map(f, nums)
map(lambda y: x + y, nums)
Toho se nejčastěji dosáhne návratem funkce, protože funkce musí bát definována v rámci oboru platnosti nelokálních proměnných, v takovém případě bude její vlastní obor platnosti obvykle menší. Toho lze také dosáhnout zakrýváním proměnných (které zmenšuje obor platnosti nelokální proměnné), i když je to v praxi méně obvyklé, protože je to méně užitečné a zakrývání se nedoporučuje. V následujícím příkladě lze x = 0
def f(y):
return x + y
def g(z):
x = 1 # lokální x zakrývá globální x
return f(z)
g(1) # g se vyčísluje na 1, ne na 2
PoužitíUzávěry se používají v jazycích, kde funkce jsou objekty první kategorie, v nichž mohou být funkce vracené jako výsledky funkcí vyššího řádu nebo předávané jako argumenty při volání jiných funkcí; pokud funkce s volnými proměnnými jsou první kategorie, pak vrácení funkce vytváří uzávěr. K těmto jazykům patří jazyky pro funkcionální programování, např. Lisp a ML, a mnoho moderních multiparadigmatických jazyků, např. Julia, Python nebo Rust. Uzávěry se také často používají pro callbacky, obzvláště pro funkce pro obsluhu události, např. v JavaScriptu, kde se používají pro interakce s dynamickými webovými stránkami. Uzávěry se mohou také používat ve stylu continuation-passing pro skrytí stavu. Konstrukty, jako jsou objekty a řídicí struktury lze tedy implementovat pomocí uzávěrů. V některých jazycích může uzávěr vzniknout, když je funkce definována uvnitř jiné funkce, a vnitřní funkce se odkazuje na lokální proměnné vnější funkce. V době běhu, kdy se provádí vnější funkce, se vytvoří uzávěr, sestávající z kódu vnitřní funkce a odkazů (upvalues) na všechny proměnné vnější funkce, které uzávěr vyžaduje. Funkce první kategorieUzávěry se obvykle vyskytují v jazycích s funkcemi první kategorie, jinými slovy v takových jazycích, kde je dovoleno předávat funkce jako argumenty, návratové hodnoty z volání funkcí, vazby na jména proměnných, atd., stejně jako jednodušší typy, např. řetězce a celá čísla. Například uvažujme následující funkci v jazyce Scheme: ; Vrátí seznam všech knih, jichž se prodalo alespoň threshold kopií.
(define (best-selling-books threshold)
(filter
(lambda (book)
(>= (book-sales book) threshold))
book-list))
Lambda výraz Uzávěr je pak předán funkci Zde je stejný příklad přepsaný v JavaScriptu, jiném oblíbeném jazyce, který má uzávěry: // Vrátí seznam všech knih, jichž se prodalo nejméně 'threshold' kopií.
function bestSellingBooks(threshold) {
return bookList.filter(book => book.sales >= threshold);
}
Operátor šipka Funkce může vytvořit uzávěr a vrátit jej, jako v tomto příkladě: // Vrátí funkci, které aproximuje derivaci funkce f
// pomocí intervalu dx, který by měl být dostatečně malý.
function derivative(f, dx) {
return x => (f(x + dx) - f(x)) / dx;
}
Protože uzávěr v tomto případě přežívá provedení funkce, která jej vytvořila, proměnné Reprezentace stavuUzávěr lze použít pro spojení funkce se sadou „soukromých“ proměnných, které přetrvávají po několik vyvolání funkce. obor platnosti proměnné zahrnuje pouze uzavřenou funkci, takže k ní nelze přistupovat z jiného programového kódu. Tyto jsou analogický na soukromý proměnné v Objektově orientované programování, a ve skutečnosti jsou uzávěry podobné stavovým funkčním objektům (nebo funktorům) s jednou metodou (operátorem) volání. Ve stavových jazycích lze tedy uzávěry používat pro implementaci vzorů pro reprezentaci stavu a skrývání informací, protože upvalues uzávěru (jeho uzavřených proměnných) mají neomezený rozsah, takže hodnota stanovená při jedno vyvolání zůstává dostupná v dalším. Takto používané uzávěry již nemají referenční průhlednost, a nejsou tedy již čistými funkcemi; přesto se však často používají v nečistých funkcionálních jazycích, jako je Scheme. Jiná použitíUzávěry mají mnoho použití:
(define foo #f)
(define bar #f)
(let ((secret-message "none"))
(set! foo (lambda (msg) (set! secret-message msg)))
(set! bar (lambda () secret-message)))
(display (bar)) ; vypíše "none"
(newline)
(foo "meet me by the docks at midnight")
(display (bar)) ; vypíše "meet me by the docks at midnight"
Poznámka: Někteří autoři nazývají uzávěr jakoukoli datovou strukturu, které váže lexikální prostředí, ale termín se obvykle používá pouze pro funkce. Implementace a teorieUzávěry se obvykle implementují speciální datovou strukturou, která obsahuje ukazatel na kód funkce a reprezentaci lexikálního prostředí funkce (tj. množinu dostupných proměnných) v okamžiku, kdy byl uzávěr vytvořen. Odkazující prostředí váže nelokální jména na odpovídající proměnné v lexikální prostředí v době vytvoření uzávěru a navíc rozšiřuje jejich životnost alespoň na dobu životnosti uzávěru. Při pozdějším zadaní uzávěru, případně s jiným lexikálním prostředím, se funkce provede s nelokálními proměnnými, které se odkazují na proměnné zachycené uzávěrem, nikoli s aktuálním prostředím. V jazycích, které alokují všechny automatické proměnné na lineárním zásobníku, je implementace plných uzávěrů obtížná. V takových jazycích jsou automatické lokální proměnné dealokovány při ukončení funkce. Uzávěr však vyžaduje, aby volné proměnné, na které se v uzávěru odkazuje, existovaly i po ukončení obklopující funkce. Tyto proměnné proto musí být alokované tak, aby přetrvaly, dokud nebudou potřeba, obvykle díky přidělování paměti na haldě nikoli na zásobníku, a jejich životnost musí být řízena tak, aby byly zachovány, dokud se budou používat všechny uzávěry, které se na ně odkazují. To vysvětluje, proč jazyky, které nativně podporují uzávěry, obvykle používají také garbage kolektor. Alternativou je ruční správa paměti nelokálních proměnných (jejich explicitní alokace na haldě a uvolnění po ukončení) nebo, pokud se používá zásobník, aby jazyk akceptoval, že určité případy použití způsobí nedefinované chování, kvůli dangling ukazatelům na uvolněné proměnnéi, jako je tomu v lambda výrazech v C++11[10] nebo vnořený funkcí v GNU C.[11] Problém funarg (problém „argumentů funkce“) popisuje obtíže při implementaci funkcí jako objektů první kategorie v programovacích jazycích založených na zásobníku, jako je např. C nebo C++. Podobně se v jazyce D verze 1 se předpokládá, že programátor ví, co dělat s delegáty a automatickými lokálními proměnnými, protože jejich reference budou neplatné po návratu z oboru platnosti, v němž jsou definovány, (automatické lokální proměnné jsou na zásobníku) – i to stále umožňuje mnoho užitečných funkčních vzorů, ale pro složité případy je nezbytná explicitní správa haldy pro proměnné. Jazyk D verze 2 to vyřešil detekcí, jaké proměnné musí být umístěny na haldě, a automatickou alokací. Protože jazyk D používá v obou verzích garbage kolektor, není potřeba sledovat použití proměnných při jejich předávání. Ve striktně funkcionálních jazycích s immutable daty (například v jazyce Erlang), je velmi snadné implementovat automatickou správu paměti (garbage collecor), protože nemohou existovat cykly v referencích na proměnné. V Erlangu jsou například všechny argumenty a proměnné alokované na haldě, ale reference na ně jsou navíc uložené na zásobníku. Po návratu z funkce jsou reference stále platné. Čištění haldy se provádí inkrementálním garbage kolektorem. V jazyce ML mají lokální proměnné lexikální obor platnosti a proto definují model podobný zásobníku, ale protože nejsou vázané na objekty ale na hodnoty, implementace může klidně kopírovat tyto hodnoty do datové struktury uzávěru za zády programátora. Jazyk Scheme, který používá systém lexikálních oborů platnosti ve stylu ALGOLu s dynamickými proměnnými a garbage kolektorem, nemá zásobníkový programovací model a netrpí omezeními jazyků založených na zásobníku. Ve Scheme se uzávěry vyjadřují přirozeně. Lambda tvar obklopuje kód, a volné proměnné jeho prostředí přetrvávat v programu, dokud k nim lze přistupovat, a tak mohou být používány stejně volně jako jiné výrazy ve Scheme.[zdroj?] Uzávěry se podobají Aktérům v Aktorovém modelu paralelních výpočtů, kde se hodnoty v lexikálním prostředí funkce nazývají acquaintances. Důležitou otázkou týkající se uzávěrů v jazycích pro paralelní programování je, zda lze modifikovat proměnné v uzávěru, a pokud ano, jak lze tyto modifikace synchronizovat. Jedno řešení poskytují aktéři.[12] Uzávěry jsou podobné funkčním objektům; transformaci uzávěrů na funkční objekty se říká defunctionalization nebo lambda lifting; viz také konverze uzávěrů.[zdroj?] Rozdíly v sémanticeLexikální prostředíProtože různé jazyky nemají vždy stejně definované lexikální prostředí, mohou mít také různé definice uzávěru. Obvyklá minimalistická definice lexikálního prostředí jej definuje jako sadu všech vazeb proměnných v oboru platnosti, což musí také uzávěry v libovolném jazyce zachycovat. Také význam vazeb proměnných je různý. V imperativních jazycích se proměnné vážou k relativním lokacím v paměti, do které se ukládají hodnoty. I když se relativní umístění vazeb v době běhu nemění, hodnota ve vázané lokaci se měnit může. Protože uzávěr v takových jazycích zachycuje vazby, jakákoli operace s proměnnou, ať je provedena z uzávěru nebo ne, se provádí na stejnou relativní lokaci v paměti. Tomu se obvykle říká zachycení proměnné „referencí“. Tento koncept ilustruje následující příklad v ECMAScriptu: // Javascript
var f, g;
function foo() {
var x;
f = function() { return ++x; };
g = function() { return --x; };
x = 1;
alert('v foo, volání na f(): ' + f());
}
foo(); // 2
alert('volání na g(): ' + g()); // 1 (--x)
alert('volání na g(): ' + g()); // 0 (--x)
alert('volání na f(): ' + f()); // 1 (++x)
alert('volání na f(): ' + f()); // 2 (++x)
Funkce V některých instancích může být výše uvedené chování nežádoucí, a je třeba připojit jiný lexikální uzávěr. V ECMAScriptu je to možné provést pomocí Příklad 1: Odkaz na nevázanou proměnnouPodle[13] var modul = {
x: 42,
getX: funkce() {return toto.x; }
}
var unboundGetX = modul.getX;
console.log(unboundGetX()); // Funkce je vyvolána v globálním oboru platnosti
// vypíše undefined, protože 'x' není definováno v globálním oboru platnosti.
var boundGetX = unboundGetX.bind(modul); // specifikují modul objektu jako uzávěr
console.log(boundGetX()); // vypíše 42
Příklad 2: Neúmyslný odkaz na vázanou proměnnouOčekávané chování v tomto příkladě by bylo, že každý odkaz musí vypsat svůj id, když se na něj klikne; ale protože proměnná 'e' je vázaná k oboru platnosti výše, a při kliknutí je líně vyhodnocena, dojde k tomu, že každá událost kliknutí emituje identifikátor posledního prvku z 'prvků' vázaných na konci for cyklu.[14] var elements = document.getElementsByTagName('a');
// Nesprávný: e je vázaný k funkci obsahující 'for' cyklus, ne uzávěr funkce "handle"
pro (var e of elements) { e.onclick = function handle() { alert(e.id);
} }
Proměnná Na druhou stranu mnoho funkcionálních jazyků, např. ML, váže proměnné přímo na hodnoty. Protože v tomto případě neexistuje žádný způsob, jak změnit hodnotu proměnné, jakmile je jednou svázána, neexistuje žádné potřebuje pro sdílení stav mezi uzávěry – prostě používají stejné hodnoty. Tento postup se obvykle nazývá zachycení proměnné „hodnotou“. Lokální a anonymní třídy Javy také spadají do této kategorie – vyžadují, aby zachycené lokální proměnné byly Některé jazyky umožňují výběr mezi zachycením hodnoty proměnné nebo odkazu na proměnnou. Například v C++11 se zachycené proměnné deklarují buď s Další skupina, funkcionální jazyky s odloženým vyhodnocováním, jako např. Haskell (programovací jazyk), svazuje proměnné k výsledkům budoucí výpočtů místo hodnot. Uvažujme tento příklad v Haskellu: -- Haskell
foo :: Fractional a => a -> a -> (a -> a)
foo x y = (\z -> z + r)
where r = x / y
f :: Fractional a => a -> a
f = foo 1 0
main = print (f 123)
Vazba Opuštění uzávěruJeště více rozdílů se objevuje v chování jiných konstruktů s lexikálním oborem platnosti, např. příkazů "Smalltalk"
foo
| xs |
xs := #(1 2 3 4).
xs do: [:x | ^x].
^0
bar
Transcript ukazují: (self foo printString) "vypíše 1"
// ECMAScript
function foo() {
var xs = [1, 2, 3, 4];
xs.forEach(function (x) { return x; });
return 0;
}
alert(foo()); // vypíše 0
Výše uvedené kusy kódu se budou chovat odlišně, protože operátor Common Lisp poskytuje konstrukci, která umožňuje vyjádřit obě výše uvedené možnosti: "Smalltalk"
foo
^[ :x | ^x ]
bar
| f |
f := self foo.
f value: 123 "chyba!"
Když je uzávěr vrácený metodou Některé jazyky jako Ruby dávají programátorovi možnost výběru, jak má být # Ruby
# Uzávěr používající Proc
def foo
f = Proc.new { return "návrat z foo v proc" }
f.call # zde řízení opouští foo
return "návrat z foo"
end
# Uzávěr používající lambda
def bar
f = lambda { return "návrat z lambda" }
f.volání # kontrola/kontrolovat does ne ponechá bar zde
return "návrat z bar"
end
puts foo # vypíše "návrat z foo z v proc"
puts bar # vypíše "návrat z bar"
V jazyce Scheme je definice a obor platnosti řídicího příkazu ; Scheme
(define call/cc call-with-current-continuation)
(define (foo)
(call/cc
(lambda (return)
(define (f) (return "návrat z foo v proc"))
(f) ; control leaves foo here
(return "návrat z foo"))))
(define (bar)
(call/cc
(lambda (return)
(define (f) (call/cc (lambda (return) (return "návrat z lambda"))))
(f) ; control does not leave bar here
(return "návrat z bar"))))
(display (foo)) ; vypíše "návrat z foo v proc"
(newline)
(display (bar)) ; vypíše "návrat z bar"
Konstrukty podobné uzávěrůmNěkteré jazyky mají vlastnosti, které se podobají chování uzávěrů. V jazycích jako je C++, C#, D, Java, Objective-C a Visual Basic .NET (VB.NET) jsou tyto vlastnosti důsledkem objektové orientace jazyka. Callbacky (C)Některé knihovny jazyka C podporují callbacky. To se někdy implementuje použitím dvou hodnot při registraci callbacku v knihovně: jeden je ukazatel na funkci a druhý zvláštní ukazatel typu Callbacky se široce používají ve widget toolkitech pro grafické uživatelské rozhraní (GUI) pro implementaci událostmi řízeného programování propojením obecných funkcí grafických widgetů (nabídek, tlačítek, zaškrtávacích polí, šoupátek, číselníků, atd.) s funkcemi specifickými pro aplikaci, které v aplikaci implementují požadované chování. Vnořené funkce a ukazatel na funkci (C)Rozšíření v překladači GCC umožňuje používat vnořené funkce[15] a pomocí ukazatelů na funkce je možné emulovat uzávěry, pokud funkce does neopustí obklopující obor platnosti. Následující příklad je chybný, protože #include <stdio.h>
typedef int (*fn_int_to_int)(int); // typ funkce int->int
fn_int_to_int adder(int number) {
int add (int value) { return value + number; }
return &add; // & operátor je zde nepovinný, protože jméno funkce v jazyce C je ukazatel ukazující na funkci
}
int main(void) {
fn_int_to_int add10 = adder(10);
printf("%d\n", add10(1));
return 0;
}
Po přesunu definice funkce #include <stdio.h>
int main(void) {
typedef int (*fn_int_to_int)(int); // typ funkce int->int
fn_int_to_int adder(int number) {
int add (int value) { return value + number; }
return add;
}
fn_int_to_int add10 = adder(10);
printf("%d\n", add10(1));
return 0;
}
Program vypíše podle očekávání Lokální třídy a lambda funkce (Java)Java povoluje třídy být definovaný v metody. Nazývají se lokální třídy. Když takový třídy nejsou pojmenovaný, jsou známou jako anonymní třídy (nebo anonymní vnitřní třídy). A lokální třída (buď pojmenovaný nebo anonymní) může se odkazuje na jména lexikálně obklopujícího třídy, anebo proměnné pouze pro čtení (vyznačené jako class CalculationWindow extends JFrame {
private volatile int result;
// ...
public void calculateInSeparateThread(final URI uri) {
// Výraz "new Runnable() { ... }" je anonymní třída implementujícíous 'Runnable' rozhraní.
new Thread(
new Runnable() {
void run() {
// Může číst lokální proměnné označené jako final:
calculate(uri);
// Může přistupovat k soukromým položkám obklopující třídy:
result = result + 10;
}
}
).start();
}
}
Zachycení proměnné Zachycení proměnných referencí lze emulovat pomocí S příchodem lambda výrazů ve verzi 8 Javy[16] uzávěr způsobí, že se výše uvedený kód bude provádět takto: class CalculationWindow extends JFrame {
private volatile int result;
// ...
public void calculateInSeparateThread(final URI uri) {
// Část code () -> { /* code */ } je uzávěr.
new Thread(() -> {
calculate(uri);
result = result + 10;
}).start();
}
}
Lokální třídy jsou jedním z typů vnitřních tříd, které se deklarují v těle metody. Java také podporuje vnitřní třídy, které se deklarují jako nestatické členy obklopující třídy,[17] a obvykle jsou jednoduše nazývány „vnitřní třídy“.[18] Jsou definovány v těle obklopující třídy a mají plný přístup k instančním proměnným obklopující třídy. Kvůli jejich vazbě na tyto instanční proměnné mohou být vnitřní třídy instanciovány pouze s explicitní vazbou na instance obklopující třída pomocí/použití speciální syntax.[19] public class EnclosingClass {
/* Definice vnitřní třídy */
public class InnerClass {
public int incrementAndReturnCounter() {
return counter++;
}
}
private int counter;
{
counter = 0;
}
public int getCounter() {
return counter;
}
public static void main(String[] args) {
EnclosingClass enclosingClassInstance = new EnclosingClass();
/* Instanciace vnitřní třídy s vazbou na instance */
EnclosingClass.InnerClass innerClassInstance =
enclosingClassInstance.new InnerClass();
for (int i = enclosingClassInstance.getCounter();
(i = innerClassInstance.incrementAndReturnCounter()) < 10;
/* inkrement není použit */) {
System.out.println(i);
}
}
}
Program vytiskne čísla od 0 do 9. Tento typ třídy se nesmí zaměňovat s vnořenou třídou deklarovanou stejně, ale s modifikátorem „static“; pak by šlo o třídu bez speciální vazby definované v obklopující třídě. Java podporuje funkce jako objekty první kategorie od verze 8. Lambda výrazy tohoto tvaru jsou považovány za typ public static void main(String[] args) {
Function<String, Integer> length = s -> s.length();
System.out.println( length.apply("Hello, world!") ); // Vypíše 13.
}
Bloky Podrobnější informace naleznete v článku Blocks_(C_language_extension).
Firma Apple zavedla bloky, forma uzávěru, jako nestandardní rozšíření jazyků C, C++, Objective-C a v Mac OS X 10.6 „Snow Leopard“ a iOS 4.0. Apple vytvořený jejich implementace dostupný pro GCC a clang překladače. Ukazatele na bloky a blokové literály jsou označeny typedef int (^IntBlock)();
IntBlock downCounter(int start) {
__block int i = start;
return [[ ^int() {
return i--;
} copy] autorelease];
}
IntBlock f = downCounter(5);
NSLog(@"%d", f());
NSLog(@"%d", f());
NSLog(@"%d", f());
Delegáti (C#, VB.NET, D)Anonymní metody a lambda výrazy v C# podporují uzávěry: var data = new[] {1, 2, 3, 4};
var multiplier = 2;
var result = data.Select(x => x * multiplier);
Také Visual Basic .NET, který se v mnoha ohledech podobá C#, podporuje lambda výrazy s uzávěry: Dim data = {1, 2, 3, 4}
Dim multiplier = 2
Dim result = data.Select(Function(x) x * multiplier)
V jazyce D jsou uzávěry implementovány pomocí delegátů, ukazatelů na funkci spojených s ukazatelem na kontext (kterým může být např. instance třídy nebo rámec zásobníku na haldě v případě uzávěrů). auto test1() {
int = 7;
return delegate() { return + 3; }; // konstrukce anonymního delegáta
}
auto test2() {
int = 20;
int foo() { return + 5; } // vnitřní funkce
return &foo; // jiný způsob konstrukce delegáta
}
void bar() {
auto dg = test1();
dg(); // =10 // ok, test1.a je v uzávěr a stále existuje
dg = test2();
dg(); // =25 // ok, test2.a je v uzávěr a stále existuje
}
D verze 1 má omezenou podporu uzávěrů. Například výše uvedený kód nebude pracovat správně, protože proměnná je na zásobníku, a po návratu z funkce test(), už ji nelze používat (nejpravděpodobněji volání foo pomocí dg() bude vracet 'náhodné' celé číslo). To lze vyřešit explicitním alokováním proměnné 'a' na haldě nebo použitím structs nebo třídy pro uložení všech potřebných uzavřených proměnných a zkonstruování delegáta z metody implementující stejný kód. Uzávěry je možné předat do jiných funkcí, ale musí se používat, pouze když referencované hodnoty jsou stále platné (například voláním jiné funkce s uzávěr jako callback parametrem), a jsou užitečné pro zapisování kódu, který zpracovává obecná data, takže v praxi toto omezení obvykle nezpůsobuje žádné potíže. Toto omezení bylo opraveno ve verzi 2 – proměnná 'a' bude automaticky umístěná na haldě, protože se používá ve vnitřní funkci, a delegace této funkce může opustit aktuální obor platnosti (přiřazením do dg nebo návratem). Jiné lokální proměnné (nebo argumenty), které nejsou referencovanými delegáty nebo které jsou pouze referencovanými delegáty, které neopustí aktuální obor platnosti, zůstávají na zásobníku, což je jednodušší a rychlejší než jejich alokace na haldě. Totéž je splněno pro metody vnitřní třídy, které referencují proměnné funkce. Funkční objekty (C++)
Jazyk C++ umožňuje vytváření funkčních objektů přetěžováním operátoru Podrobnější informace naleznete v článku Anonymní_funkce#C++_(od_C++11).
void foo(string myname) {
int y;
vector<string> n;
// ...
auto i = std::find_if(n.begin(), n.end(),
// toto je lambda výraz:
[&](const string& s) { return s != myname && s.size() > y; }
);
// 'i' je nyní buď 'n.end()' anebo ukazuje na první řetězec v 'n',
// který není roven 'myname' a jehož délka je větší než 'y'
}
Inline agenti (Eiffel)V jazyce Eiffel lze definovat uzávěry pomocí inline agentů. Inline agent je objekt reprezentující funkci definovaný uvedením kódu funkce v-řádek/vedení. Například v ok_button.click_event.subscribe (
agent (x, y: INTEGER) do
map.country_at_coordinates (x, y).display
end
)
Argument funkce Hlavním omezením agentů v Eiffelu, které je odlišuje od uzávěrů v jiných jazycích, je, že se nemohou odkazovat na lokální proměnné z obklopujícího oboru platnosti. Toto designové rozhodnutí zabraňuje nejednoznačnosti, když hovoříme o hodnotě lokální proměnné v uzávěru – má to být nejnovější hodnota proměnné nebo hodnota zachycená, když byl agent vytvořen? Z těla agenta lze přistupovat pouze ke Rezervované slovo __closure v C++BuilderEmbarcadero C++Builder má rezervované slovo
Standardní jazyk C umožňuje použít typedef void (*TMyFunctionPointer)( void );
Podobným způsobem lze deklarovat typedef void (__closure *TMyMethodPointer)();
OdkazyPoznámky
ReferenceV tomto článku byl použit překlad textu z článku Closure (computer programming) na anglické Wikipedii.
Související článkyExterní odkazy
|
Portal di Ensiklopedia Dunia