Lukkingsfunksjon

En nedleggelse (eller fullføring av funksjon ) er et konsept fra funksjonell programmering . Den beskriver en anonym funksjon som inneholder tilgang til opprettelseskonteksten . Når den kalles, får funksjonen tilgang til denne opprettelseskonteksten. Denne konteksten (minneområde, status) kan ikke refereres utenfor funksjonen, dvs. H. ikke synlig.

En lukking inneholder en referanse til funksjonen og den delen av skapelseskonteksten som brukes av den - funksjonen og den tilhørende minnestrukturen er uadskillelig lukket i en referanse (lukket term). Det er som et objekt med attributter og metoder: det inneholder en implisitt identitet, en tilstand og en oppførsel.

I programmeringsspråksyntaks oppnås dette ofte av to nestede funksjoner - den indre hovedfunksjonen er lukket (lukket) av en annen funksjon. Denne avslutningsfunksjonen inneholder den nødvendige minnestrukturen (se eksempler nedenfor). Det er konstruert på en slik måte at når det kalles, gir det en referanse til den indre funksjonen sammen med de nødvendige variablene (minnestrukturen). Strengt tatt er ikke lukkingen den indre funksjonen alene her, men sammenføyningen av (indre) funksjon og variabel tilstand som referansen peker på.

En lukking kan også sees på som et objekt som vanligvis bare har en metode. Hvis lukkingen opprettes sammen med andre lukkinger med samme kontekst, er det et objekt med flere metoder. Variablene fra genereringsområdet som inngår i lukkingen kan brukes som attributter av lukkingen.

opprinnelse

Nedleggelser er et konsept som kommer fra de funksjonelle programmeringsspråk , dukket opp for første gang i Lisp , og var fullt støttet for første gang i sin dialekt Scheme . Som et resultat ble den også støttet i de fleste av de senere funksjonelle programmeringsspråkene ( f.eks. Haskell , Ocaml ).

Fordeler og funksjoner

Med nedleggelser kan det opprettes områder som ikke er synlige, men som kan endres på en kontrollert måte, for eksempel kan datainnkapsling eller karriing implementeres.

Å lage en nedleggelse er mye mindre arbeid enn å lage en klasse med bare en metode. Etter et objektorientert syn er lukkinger egnet for rask opprettelse av en objektlignende struktur uten klasse. Ofte brukes en anonym funksjon som den indre metoden.

I et rent funksjonelt programmeringsspråk kan en lukking alltid brukes når den enten skal kalles som en funksjon i seg selv eller skal inkluderes som en parameter i et funksjonsanrop. I sistnevnte tilfelle kan den fungere som en tilbakekallingsfunksjon generert ved kjøretid og dermed muliggjøre et applikasjonsprogram å manipulere sin egen kontrollflyt i betydelig grad i løpet av kjøretiden. Imidlertid blir dette vanligvis bare mulig på en praktisk fornuftig måte ved hjelp av et lukkesystem. Dette faktum er det didaktiske problemet med å få uerfarne programmerere til å forstå hvordan man bruker nedleggelser.

Eksempel i pseudokode

I det følgende eksemplet mutterfunktiondefineres først en funksjon . Denne funksjonen angir en lokal variabel som heter kuchentypog definerer en lokal funksjon som heter kindfunktion.

funktion mutterfunktion {
    setze kuchentyp = 'Apfelkuchen'

    funktion kindfunktion {
        gib_aus 'Ich esse #{kuchentyp}'
    }

    gib_zurück kindfunktion
}

Når den kalles, returnerer mutterfunktionden lokale funksjonen kindfunktion(ikke resultatet!). (Dette er teknisk også kjent som en funksjonspeker i ikke-funksjonelle programmeringsspråk som C og slektninger. En skrevet funksjonspeker kalles en delegat .)

setze meinkuchen = rufe_auf mutterfunktion

meinkuchenFunksjonen er kindfunktiontilordnet den globale variabelen .

rufe_auf meinkuchen

Den påfølgende samtalen til meinkuchenvil derfor kindfunktionutføres. Selv om ingen global variabel kuchentypeksisterer, er kindfunktiondet streng 'Ich esse Apfelkuchen' , fordi de kan få tilgang til deres skapelse i hvilken sammenheng variabelen kuchentypå 'Apfelkuchen'bli definert. Den avgjørende faktoren her er at selv om mutterfunktionen verdi allerede er returnert - konteksten faktisk ikke lenger eksisterer - den kan kindfunktionnås - kindfunktionså det er en lukkingsfunksjon.

[Ausgabe:] Ich esse Apfelkuchen

Med en endring i koden av verdien av den variable blir anzahl_kuchenden mutterfunktionøkt med hver enkelt tilgang til lukkefunksjonen etter en, noe som kan realiseres en teller. Verdien i anzahl_kuchener beskyttet mot manipulasjon og kan bare essenøkes med.

funktion mutterfunktion {
    setze anzahl_kuchen = 0

    funktion kindfunktion {
        setze  anzahl_kuchen = anzahl_kuchen + 1
        gib_aus 'Ich esse #{anzahl_kuchen} Kuchen'
    }

    gib_zurück kindfunktion
}

Med flere anrop av foreldrefunksjonen fra andre programdeler, kan den faktisk ikke lenger synlige verdien av den lokale variabelen <nummer_kake> bare nås indirekte, og (bare) innenfor kindfunktion(innkapslede) beregninger med ellers uforanderlige verdier kan bæres ut - dette vises av de viktigste fordelene ved nevnte nedleggelser:

setze essen = rufe_auf mutterfunktion
rufe_auf essen
rufe_auf essen
rufe_auf essen
Ich esse 1 Kuchen
Ich esse 2 Kuchen
Ich esse 3 Kuchen

Direkte tilgang til variabelen anzahl_kuchener beskyttet på denne måten; verdien kan (som i eksemplet) eller kunne ikke overføres direkte til omverdenen. Under ingen omstendigheter kan verdien endres eksternt, så lukkinger gir mer tilgangsbeskyttelse enn felt i en klasse som er erklært som "privat", for eksempel i Java eller C # , som for eksempel lett kan omgåes med refleksjon .

Hvordan du tolker dette, avhenger sterkt av ditt eget perspektiv på programmeringsspråk. Fra et objektorientert synspunkt tar foreldrefunksjonen rollen som en klasse, nærmere bestemt et objekt (forekomsten av en klasse), og fra et objektorientert synspunkt innkapsler barnvariabler med barnefunksjon (er) ) for å danne en enhet.

Sett annerledes implementeres en slags "minnet" på tvers av samtaler i funksjonene, i likhet med en statisk variabel, bare kraftigere. Sett litt annerledes kan dette også sees på som en endring i kontrollflyten, som eksemplet ovenfor viser veldig bra. Lister kan for eksempel implementeres som et funksjonsanrop, siden et annet resultat kan leveres for hver samtale (på grunn av "minnet"). C # bruker dette som en spesiell sak, for eksempel når man implementerer "yield return". For hver samtale er det neste elementet av en opptelt type, for eksempel en liste, så å si "lat" , dvs. H. For å spare ressurser bare for å bli returnert når det er nødvendig.

Konseptuelle krav til nedleggelser i programmeringsspråk

Som nevnt representerer nedleggelser et mønster av funksjonell programmering; de er ofte vanskelige å forstå for programmerere av ikke bare funksjonelle programmeringsspråk, selv om de kan implementeres på et økende antall programmeringsspråk.

Følgende konseptuelle "byggesteiner" er nødvendige for å gjøre en lukking mulig på et programmeringsspråk.

1. Funksjoner må være tillatt som returobjekter for en annen funksjon, i det minste via brukte elementer som funksjonspekere , delegater eller lambdauttrykk. Dette blir også referert til som førsteklasses funksjoner . (Det motsatte gjelder spesielt når funksjoner bare kan vises og brukes som en type navngitt kommando).

2. I eksemplet ovenfor må den indre funksjonen kunne få tilgang til variablene til den ytre funksjonen (samtalemiljø). I motsetning til lokale variabler blir disse variablene også referert til som "gratis variabler" fra den interne funksjonens synspunkt.

3. Kompilatoren må kunne gjenkjenne at verdien (tilstanden) til variabelen kreves utenfor dens faktiske omfang, og aktivt ta hensyn til dette under kompilering. Teknisk sett lagres disse variablene vanligvis ikke lenger på bunken, men dette løses annerledes, f.eks. B. ved å faktisk lage en (anonym) klasse som inkluderer en forekomst i bakgrunnen som inneholder de nødvendige (medlems) variablene og den indre funksjonen (som en medlemsfunksjon).

Først nå har alle byggesteinene kommet sammen for å skape en forkortet, men mer teknisk definisjon av begrepet nedleggelse , nærmere bestemt av leksikale nedleggelser i snevre forstand:

Avslutninger er derfor en programmeringsteknikk eller strukturer for å implementere leksikalt omfang med gratis variabler på språk med førsteklasses funksjoner .

Dynamiske og leksikale lukkinger

Den første implementeringen av nedleggelser oppsto fra måten Lisp implementerte utførelsesmiljøer på. I de første Lisp-implementeringene var det ingen leksikalsk kopiering . Utførelsesmiljøet til en instruksjon besto av en såkalt A-liste med variable bindinger, som kunne nås via en enkelt referanse. En lukking over en funksjon besto da av et par, bestående av funksjonsdefinisjonen og henvisningen til A-listen som var gyldig på det tidspunktet lukkingen ble definert. Dette paret generert av Lisp-funksjonen FUNCTION er en dynamisk nedleggelse med det historiske navnet FUNARG (FUNctional ARGument). Hvis FUNARG ble utført senere, ble dette gjort i sammenheng med A-listen som ble tatt med i stedet for i sammenheng med den gjeldende A-listen.

Den leksikale kartleggingen som brukes i dag i Lisp som på alle andre språk, fører til den leksikale nedleggelsen, som også er funksjonell i kompilerte språk. Det oppstår bare når kompilatoren griper aktivt inn ved å identifisere funksjonens referanser til variablene som er ledige innenfor og utenfor den, og som genererer kode som kombinerer disse relasjonene med funksjonen til en lukking når den returneres fra definisjonskonteksten. Dette skjer før denne funksjonen - nå som avslutning - blir gjort tilgjengelig for den som ringer. Siden denne variable bindingen ikke lenger er leksikalt bundet, kan den ikke forbli på bunken, men plasseres på dyngen av kjøretidssystemet. Hvis flere lukninger dannes samtidig med samme variabelbinding, sørger kjøretidssystemet for at den samme bunkebaserte kopien av denne variabelbinding brukes i begge lukkinger.

Implementeringer

Det er også ikke-funksjonelle programmeringsspråk som støtter denne funksjonen. Disse inkluderer Ada , C ++ (fra C ++ 11), C # , Go , Groovy , Java , JavaScript , Lua , Object Pascal (Delphi), PHP , Perl , Python , Ruby , Smalltalk , Swift og Visual Basic. NET . Apple har utvidet gcc og clang til å omfatte lukninger - kalt block literals - for C og foreslo dette for standardisering.

Eksempler på implementeringer

Vanlig Lisp

Dette eksemplet bruker en lukking for å aktivere et elegant databasespørsmål. Avslutningen er gitt av funksjonen name-is. Den spesielle funksjon lambda skaper en navnløs funksjon i hvor verdien av navn feltet er nkontrollert for likestilling med en tegnstreng . Samtalen (name-is "Elke")tilveiebringer derfor en lukning som en forbindelse fra den anonyme funksjon og den variable kobling fra ntil tegnstrengen "Elke". Dette kan sjekke en datapost med samme navn som "Elke". Avslutningen kan overføres direkte til funksjonen filter, som deretter bruker den og returnerer resultatet.

(defparameter *dbase*
    '(("Elke"  "1.1.1980") ("Gabi"  "2.3.1981") ("Heidi" "4.5.1982")
      ("Gabi"  "5.6.1983") ("Uschi" "7.8.1984")))

(defun get-name (list)
    (first list))

(defun name-is (name)
    (lambda (list)
        (equal (get-name list) name)))

(defun filter (predicate list)
    (remove-if-not predicate list))

Disse definisjonene gjør nå følgende elegante spørring mulig:

(print (filter (name-is "Gabi") *dbase*))

Det skal forstås slik: Funksjonsanropet (name-is "Gabi")gir en lukking. Her er det en kombinasjon av sammenligningskoden (equal (get-name list) name)fra funksjonen name-isog bindingen av tegnstrengen "Gabi"til variabelen name. Dette er semantisk et spørsmål om spørringen (equal (get-name list) "Gabi"). Denne sammenligningen sendes som lukking av funksjonen filtersom bruker denne sammenligningen. Å utføre denne filtreringen fører deretter til resultatet:

(("Gabi" "2.3.1981") ("Gabi" "5.6.1983"))

Perle

Konteksten til et hvilket som helst kodefragment bestemmes blant annet av symbolene som er tilgjengelige:

# pragma
use strict;

sub function {
    # Argumente in benannte Variablen kopieren
    my ($var1, $var2) = @_;

    # block code
}

I det eksempel som er vist ovenfor, er variable $var1og er $var2gyldig og synlig på hvert punkt i funksjon. Når du avslutter funksjonen, blir de ryddet opp sammen med den forlatte blokken ("gå utenfor omfanget" ) og er da ukjente. Enhver ytterligere tilgang vil være en feil.

Nedleggelser gir nå muligheten til å utvide omfanget av slike variabler utover deres offisielle slutt. For å gjøre dette, defineres en funksjon ganske enkelt i omfanget som bruker relevante variabler:

# pragma
use strict;

sub function {
    my ($var1, $var2) = @_;
    return sub { print "Vars: $var1, $var2.\n" };
}

my $f = function("Hallo", 8);
my $g = function("bar", "Y");

# Aufruf von $f
$f->();

# Aufruf von $g
$g->();

Når funksjonen functionavsluttes, oppdager kjøretidssystemet nå at referanser til blokkvariablene $var1og fortsatt $var2eksisterer - returverdien er en anonym underrutine , som igjen inneholder referanser til blokkvariablene. $var1og $var2blir derfor beholdt med sine nåværende verdier. Fordi funksjonen bevarer variablene på denne måten, blir den en lukking.

Med andre ord, selv etter å ha forlatt variabelens faktiske gyldighetsområde, kan du utføre samtalen $f->()og samtalen når som helst, og $g->()resultatet vil alltid vises verdiene til variablene som var gyldige da funksjonene ble definert.

Dette gir produksjonen:

Vars: Hallo, 8.
Vars: bar, Y.

Du kan ikke lenger endre disse verdiene fordi variablene utenfor lukkingen ikke lenger er tilgjengelige. Men dette er hovedsakelig på grunn av definisjonen av funksjonen: Selvfølgelig kunne lukkingen ikke bare ha sendt ut verdiene, men også redigere dem eller gjøre dem tilgjengelige for ringekoden ved referanse. I den følgende varianten introduseres for eksempel funksjoner for inkrementering og dekrementering:

# pragma
use strict;

# function
sub function {
    my ($var1, $var2) = @_;

    return (
        sub {print "Vars: $var1, $var2.\n"},
        sub {$var1++; $var2++;},
        sub {$var1--; $var2--;}
    );
}

# call the function
my ($printer, $incrementor, $decrementor) = function(3,5);
# use closures
$printer->();
$incrementor->();
$printer->();
$incrementor->();
$incrementor->();
$printer->();

Dette gir produksjonen:

Vars: 3, 5.
Vars: 4, 6.
Vars: 6, 8.

Nedleggelser kan for eksempel brukes til å kapsle tilgang til sensitive data.

python

Følgende er et enkelt eksempel på en teller i Python som fungerer uten en (navngitt) beholder som lagrer gjeldende telleravlesning.

def closure():
    container = [0]

    def inc():
        container[0] += 1

    def get():
        return container[0]

    return inc, get

I eksemplet closureopprettes to funksjonsobjekter i funksjonen, som begge containerrefererer til listen fra deres respektive høyere nivå. Hvis closurefunksjonen er behandlet (etter en samtale) og de to returnerte funksjonsobjektene fortsetter å bli referert, containerfortsetter listen å eksistere selv om lukkingsområdet allerede er utgått. På denne måten er listen bevart i et anonymt omfang. Listen er ikke containertilgjengelig direkte. Hvis de to funksjonene er objekter incog getikke lenger er referert til, forsvinner beholderen også.

Avslutningen i forrige eksempel brukes deretter på følgende måte:

>>> i, g = closure()
>>> g()
0
>>> i()
>>> i()
>>> g()
2

OCaml

OCaml tillater dette på følgende måte:

let counter, inc, reset =
    let n = ref 0 in
        (function () -> !n),    	 (* counter  *)
        (function () -> n:= !n + 1), (* incrementor *)
        (function () -> n:=0 )     	 (* reset  *)

nå kan telleren brukes som følger:

# counter();; (* ergibt 0 *)
# inc();;
# counter();; (* ergibt 1 *)
# inc();inc();inc();;
# counter();; (* ergibt 4 *)
# reset();;
# counter();; (* ergibt 0 *)
# n;; (* n ist gekapselt *)
Unbound value n

I stedet for et heltall kan selvfølgelig alle objekter eller variabler av hvilken som helst type innkapsles på denne måten.

JavaScript

I funksjonen f1 er en annen funksjon f2 definert som lukking;

let f1 = function() {      // eine äußere Funktion f1 definieren ...
    let wert = 22;          // ... und darin einen Namensraum erstellen.

    let f2 = function() {  // eine innere Funktion definieren, ...
        return wert;          // ... die den Namensraum nach außen reicht.
    }

    return f2;   // f2 durch f1 zurückgeben, womit f2 zum closure wird.
}

let a = f1(); // a ist die von f1() zurückgegebene closure-Funktion, ...
console.log(f1()); // ... also: function() {return wert;}
console.log(typeof wert); // ist undefined
console.log(a()); // ergibt 22
console.log(f1()()); // ergibt 22, f2() ist hier aber nicht abrufbar

Ovennevnte eksempel er formulert annerledes, den indre funksjonen kalles nå direkte:

let f3 = function() {
    let wert = 23;

    // die Funktion f3 gibt gleich die closure-Funktion zurück!
    return function() {
        return wert;
    };
}

let b = f3(); // b ist wieder die von f3() zurückgegebene Funktion ...
console.log(b()); // ... und liefert jetzt als Ergebnis 23
console.log(b); // b bleibt aber weiterhin ein Funktionsaufruf!
console.log(f3()()); // liefert ebenfalls 23

Den innebygde funksjonen fungerer som leverandør av verdien som er definert i funksjonen på høyere nivå.

Den overordnede funksjonen kan også defineres som en anonym funksjon :

let wert = 24;

let c = (function() { // die äußere als anonyme Funktion und ...
    return wert;        // ... darin die innere Funktion definieren.
}());                   // die Funktion jetzt noch mit (); aufrufen.

console.log(c);         // ergibt 24

Avslutningen kan også opprettes med en konstruktorfunksjon:

let d = (new Function("return wert;"))();   // mit einem Konstruktor definieren und aufrufen

Lua

Lua har innebygd og, når det gjelder programmering, også intuitivt brukbar støtte for nedleggelser, hvis implementering er lik den i Python:

function adder(x)      -- Funktionserzeuger
    return function(y) -- anonyme, zu adder private Funktion
        return x+y     -- x stammt hier aus dem äußeren Kontext
    end
end

Et eksempel på bruk vil se slik ut:

add2 = adder(2)    -- hier wird die Closure erzeugt
print(add2(10))    --> Ausgabe 12
print(add2(-2))    --> Ausgabe 0

En nedleggelsesimplementering i Lua er beskrevet i.

Erlang

Erlang som et funksjonelt språk har også nedleggelser, som imidlertid kalles funs (singular fun , from function ).

do_something(Fun) -> Fun(4).

main() ->
    Var = 37,
    F = fun(N) -> Var + N end.
    Result = do_something(F).
    % Result =:= 41 =:= 37 + 4

C #

C # støtter nedleggelser i form av delegater .

private static Action CreateClosure()
{
    // Deklaration einer Variablen im lokalen Kontext
    var x = 0;

    // Erstellung eines Closure-Delegate mit Hilfe eines Lambda-Ausdrucks
    Action closure = () => Console.WriteLine(x);

    // Änderung am lokalen Kontext
    x = 1;

    // Rückgabe der Closure in den übergeordneten Kontext
    return closure;
}

static void Main()
{
    var closure = CreateClosure();

    // Im globalen Kontext
    // Variable x wird nur noch innerhalb der Closure referenziert

    // Führe Closure aus; Schreibt "1" auf die Konsole
    closure();
}

C ++ 14

C ++ støtter Closures betyr lambdauttrykk (fra C ++ 11) som strekker seg i funksjonsobjekter kan kalles radiodører, av typen std :: funksjonskapsler.

#include <string>
#include <iostream>

auto create_closure() {
    std::string kuchen("Apfelkuchen");

    // Lokale Variablen werden hier als Kopie in das Funktionsobjekt übertragen
    return [=]() { std::cout << "Ich esse " << kuchen << std::endl; };
}

int main() {
    auto closure = create_closure();
    closure();

    return 0;
}

Ved hjelp av nøkkelordet mutable , kan det opprettes en reell lukking fra en lambda-funksjon, som ikke bare har sine egne variabler, men som også kan endre dem (variabelen "anzahl_kuchen" i den ytre blokken endres ikke, men bare en kopi av det):

#include <iostream>

auto mutterfunktion() {
    int anzahl_kuchen = 0;

    // Die übernommene Kopie der Variable kann hier zusätzlich ihren Wert verändern.
    return [=]() mutable { std::cout << "Ich esse " << ++anzahl_kuchen << " Kuchen.\n"; };
}

int main() {
    auto essen = mutterfunktion();

    essen();
    essen();
    essen();

    return 0;
}

Utgave av dette programmet:

Ich esse 1 Kuchen.
Ich esse 2 Kuchen.
Ich esse 3 Kuchen.

Java

I Java , fra versjon 8, er det også mulig å lukke, der noen spesifikke språkforutsetninger om lambdauttrykk må overholdes. Følgende kode vil for eksempel ikke kompilere .

private static Function<String, Supplier<String>> generator = kuchenname -> {
    int zähler = 0;
    return () -> "Ich esse " + zähler++ + " " + kuchenname; // Fehler: zähler kann nicht geändert werden
};

public static void main(String[] args) {
    Supplier<String> käsekuchen = generator.apply("Käsekuchen");

    System.out.println(käsekuchen.get());
    System.out.println(käsekuchen.get());
    System.out.println(käsekuchen.get());
}

I Java kan koden i et lambdauttrykk lese variablene til den omsluttende metoden, men kan ikke endre dem. I eksemplet ovenfor prøver koden til den returnerte leverandøren i++å endre verdien på en variabel, noe som forårsaker en kompilatorfeil. For å omgå denne begrensningen, må data som endres være innkapslet i objekter, for eksempel med AtomicInteger:

private static Function<String, Supplier<String>> generator = kuchenname -> {
    AtomicInteger zähler = new AtomicInteger(0);
    return () -> "Ich esse " + zähler.getAndIncrement() + " " + kuchenname;
};

public static void main(String[] args) {
    Supplier<String> käsekuchen = generator.apply("Käsekuchen");

    System.out.println(käsekuchen.get());
    System.out.println(käsekuchen.get());
    System.out.println(käsekuchen.get());
}

Koden korrigert på denne måten kompilerer fordi referansen til tellerobjektet i lambdauttrykket forblir uendret. Resultatet er da:

Ich esse 0 Käsekuchen
Ich esse 1 Käsekuchen
Ich esse 2 Käsekuchen

PHP

PHP støtter nedleggelser fra versjon 5.3.0 i form av anonyme funksjoner.

Teknisk sett løser PHP implementeringen av denne funksjonaliteten med sin egen "Closure" -klasse.

$mutterfunktion = function() {
    $anzahl_kuchen = 0;

    $kindfunktion = function() use (&$anzahl_kuchen) {
        $anzahl_kuchen = $anzahl_kuchen + 1;
        print "Ich esse {$anzahl_kuchen} Kuchen\n";
    };

    return $kindfunktion;
};

$essen = $mutterfunktion();
$essen();
$essen();
$essen();

Resultatet av samtalene er som følger:

Ich esse 1 Kuchen
Ich esse 2 Kuchen
Ich esse 3 Kuchen

Fra og med PHP 7.0 vil nedleggelser også støttes i form av anonyme klasser.

$essen = new class() {
    private $anzahl_kuchen = 0;

    public function __invoke() {
        $this->anzahl_kuchen = $this->anzahl_kuchen + 1;
        print "Ich esse {$this->anzahl_kuchen} Kuchen\n";
    }
};

$essen();
$essen();
$essen();

Begge implementeringene gir identiske utganger.

Rust

Rust støttet allerede nedleggelser fra versjon 0.1, opp til Rust 1.26 (publisert 10. mai 2018), nedleggelser måtte returneres fra funksjoner via en peker til haugminnet Box.

fn mutterfunktion() -> Box<FnMut() -> ()> {
    let mut anzahl_kuchen = 0;

    let kindfunktion = move || {
        anzahl_kuchen += 1;
        println!("Ich esse {} Kuchen", anzahl_kuchen);
    };

    // [Ex.1] Fehler wenn anzahl_kuchen nicht Copy implementieren würde (s.u.)
    // println!("Jetzt ist die Anzahl der Kuchen: {}", anzahl_kuchen);

    return Box::new(kindfunktion);
}

fn main() {
    let mut essen = mutterfunktion();
    essen();
    essen();
    essen();
}

Produksjon:

Ich esse 1 Kuchen
Ich esse 2 Kuchen
Ich esse 3 Kuchen

I Rust 1.26 ble impl Traitsyntaksen stabilisert, som Box::new()muliggjør den samme koden uten indireksjon via heapminne ( ):

fn mutterfunktion() -> impl FnMut() -> () {
    let mut anzahl_kuchen = 0;

    move || {
        anzahl_kuchen += 1;
        println!("Ich esse {} Kuchen", anzahl_kuchen);
    }
}

fn main() {
    let mut essen = mutterfunktion();
    essen();
    essen();
    essen();
}

mutterfunktion()Den Fnegenskap “redskap” returverdien , hvorved den eksakte type av returverdien bare bestemmes når funksjonen blir brukt.

Dette differensiert Rust mellom funksjonspekere og lukkeanordninger, samt ulike typer lukke: Fn, FnMutog FnOnce. En Fnlukking kan ikke endre konteksten den kalles i. A FnMut-Closure kan bare endre variabelen i konteksten hvis den er mutmerket som . A FnOnce-Closure forbruker variabelen som er opprettet i konteksten. Dette kunne essen()bare kalles nøyaktig én gang - på slutten av den første nedleggelsen kjører destruktoren anzahl_kuchenog variabelen er ikke lenger tilgjengelig.

Hvis [Ex.1]kommentert, er utdataene:

Jetzt ist die Anzahl der Kuchen: 0
Ich esse 1 Kuchen
Ich esse 2 Kuchen
Ich esse 3 Kuchen

Den movenøkkelordet brukes til å anzahl_kuchenindikere eierskap av variable . Siden variabelen vår kan anzahl_kuchenkopieres (variabler av typen u32implementerer Copy-Trait), kan vi fortsatt bruke variabelen i foreldrefunksjonen etter at den faktiske verdien av lukkingen er overført. Den anzahl_kuchenkopieres, dvs. H. selv om vi allerede har satt tallet til 1 i koden, sender utgangen fremdeles 0 fordi det er en komplett kopi av variabelen. Hvis typen anzahl_kuchenikke kan kopieres, utsteder kompilatoren en feil.

litteratur

  • Ralf H. Güting, Martin Erwig, oversetterkonstruksjon . Springer, 1999, ISBN 3-540-65389-9
  • Damian Conway , Objektorientert Perl
  • Oliver Lau, Andreas Linke, Torsten T. Will: Variabler å gå - Nedleggelser i gjeldende programmeringsspråk. I: c't , 17/2013, s. 168ff.

weblenker

Individuelle bevis

  1. Lukkingsbasert tilstand: C #
  2. ^ John McCarthy et al.: Lisp 1.5 Programmers Manual . (PDF) softwarepreservation.org; åpnet 12. mars 2014.
  3. ^ John Barnes: Begrunnelse for Ada 2005
  4. Stengninger i Java
  5. Stengninger i JavaScript (engelsk)
  6. Craig Stuntz: Understanding Anonymous Methods ( Memento of the original from 6. juni 2009 i Internet Archive ) Info: Arkivkoblingen ble satt inn automatisk og har ennå ikke blitt sjekket. Vennligst sjekk originalen og arkivlenken i henhold til instruksjonene, og fjern deretter denne meldingen. @1@ 2Mal: Webachiv / IABot / blogs.teamb.com
  7. Barry Kelly: Tiburon: moro med generiske og anonyme metoder .
  8. N1370: Apples utvidelser til C (PDF; 69 kB)
  9. Implementeringen av Lua 5.0
  10. Dustin Campbell: What's In A Closure. (Ikke lenger tilgjengelig online.) 9. februar 2007, arkivert fra originalen 15. august 2014 ; åpnet 12. april 2014 . Info: Arkivkoblingen ble satt inn automatisk og har ennå ikke blitt sjekket. Vennligst sjekk originalen og arkivlenken i henhold til instruksjonene, og fjern deretter denne meldingen. @1@ 2Mal: Webachiv / IABot / diditwith.net
  11. ^ Lambda-funksjoner. Hentet 17. oktober 2015 .
  12. ^ Anonyme funksjoner. Hentet 19. mai 2015 .
  13. ^ Stengningsklassen. Hentet 19. mai 2015 .
  14. Wat Joe Watkin, Phil Sturgeon: Anonyme klasser. 22. september 2013, åpnet 19. mai 2015 .