luie evaluatie van Functieargumenten

Rapporteer een bug

als u een probleem ontdekt met deze pagina, klik dan hier om een Bugzilla probleem aan te maken.

verbeter deze pagina

snel forken, online bewerken en een pull request voor deze pagina indienen.Vereist een ingelogd GitHub account. Dit werkt goed voor kleine veranderingen.Als u grotere wijzigingen wilt aanbrengen, kunt u overwegen een lokale kloon te gebruiken.

door Walter Bright, http://www.digitalmars.com/d

luie evaluatie is de techniek van het niet evalueren van een expressionunless en totdat het resultaat van de expressie is vereist.De && | / / en ?: operators zijn de conventionele manier om luie evaluatie:

void test(int* p){ if (p && p) ...}

de tweede uitdrukking p wordt niet geëvalueerd tenzij pis niet null is.Als de tweede expressie niet lui werd geëvalueerd, zou het een runtime-fout genereren als p null was.

hoewel van onschatbare waarde, hebben de luie evaluatieoperatoren aanzienlijke beperkingen. Overweeg een logfunctie, die een bericht logt, en kan worden in-en uitgeschakeld tijdens runtime op basis van een globalvalue:

void log(const(char) message){ if (logging) fwritefln(logfile, message);}

vaak zal de berichtstring tijdens runtime worden geconstrueerd:

void foo(int i){ log("Entering foo() with i set to " ~ toString(i));}

terwijl dit werkt, is het probleem dat het bouwen van de messagestring gebeurt ongeacht of loggen is ingeschakeld of niet.Met toepassingen die zwaar gebruik maken van logging, kan dit wordeneen verschrikkelijke afvoer op de prestaties.

een manier om dit te verhelpen is door luie evaluatie te gebruiken:

void foo(int i){ if (logging) log("Entering foo() with i set to " ~ toString(i));}

maar dit schendt de inkapselingprincipes door de details van logging aan de gebruiker bloot te stellen. In C wordt dit probleem vaak opgelost met behulp van een macro:

#define LOG(string) (logging && log(string))

maar dat is het probleem. Preprocessor macro ‘ s hebben bekende tekortkomingen:

  • de logboekvariabele wordt weergegeven in de naamruimte van de gebruiker.
  • macro ‘ s zijn onzichtbaar voor symbolische debuggers.
  • macro ‘ s zijn uitsluitend globaal en kunnen niet worden bestreken.
  • macro ‘ s kunnen geen klasse-leden zijn.
  • macro ‘ s kunnen hun adres niet laten opnemen, dus kunnen ze niet indirect worden doorgegeven zoals functies dat kunnen.

een robuuste oplossing zou een manier zijn om luie evaluatie van functieparameters uit te voeren. Een dergelijke manier is mogelijk in de programmeertaal D met behulp van een gedelegeerde parameter:

void log(const(char) delegate() dg){ if (logging) fwritefln(logfile, dg());}void foo(int i){ log( { return "Entering foo() with i set to " ~ toString(i); });}

nu, de string gebouw expressie wordt alleen geëvalueerd als loggingis true, en encapsulation wordt gehandhaafd. Het enige probleem is dat weinig expressies zullen willen afbreken met { return exp;}.

dus D gaat een kleine, maar cruciale stap verder (voorgesteld door Andrei Alexandrescu).Elke expressiekan impliciet worden geconverteerd naar een gedelegeerde die ofwel void of het type van de expressie retourneert.De delegate declaration wordt vervangen door de lazy storage class(voorgesteld door Tomasz Stachowiak).De functies worden dan:

void log(lazy const(char) dg){ if (logging) fwritefln(logfile, dg());}void foo(int i){ log("Entering foo() with i set to " ~ toString(i));}

wat onze originele versie is, behalve dat de string nu niet is geconstrueerd tenzij logging is ingeschakeld.

elke keer dat er een herhalend patroon wordt gezien in code, betekent het kunnen verwijderen van dat patroon en het inkapselen dat we de complexiteit van de code kunnen verminderen, en dus bugs. Het meest voorkomende voorbeeld hiervan is de functie zelf.Luie evaluatie maakt inkapseling van een groot aantal andere patronen mogelijk.

voor een eenvoudig voorbeeld, stel dat een uitdrukking afteltijden moet worden geëvalueerd. Het patroon is:

for (int i = 0; i < count; i++) exp;

dit patroon kan worden ingekapseld in een functie met behulp van luie evaluatie:

void dotimes(int count, lazy void exp){ for (int i = 0; i < count; i++) exp();}

het kan worden gebruikt als:

void foo(){ int x = 0; dotimes(10, write(x++));}

die zal afdrukken:

0123456789

complexere door de gebruiker gedefinieerde besturingsstructuren zijn mogelijk.Hier is een methode om een switch-achtige structuur te maken:

bool scase(bool b, lazy void dg){ if (b) dg(); return b;}/* Here the variadic arguments are converted to delegates in this special case. */void cond(bool delegate() cases ...){ foreach (c; cases) { if (c()) break; }}

die kan worden gebruikt als:

void foo(){ int v = 2; cond ( scase(v == 1, writeln("it is 1")), scase(v == 2, writeln("it is 2")), scase(v == 3, writeln("it is 3")), scase(true, writeln("it is the default")) );}

die zal afdrukken:

it is 2

diegenen die bekend zijn met de Lisp-programmeertaal zullen enige scherpe parallellen met Lisp-macro ‘ s opmerken.

voor een laatste voorbeeld is er het gemeenschappelijke patroon:

Abc p;p = foo();if (!p) throw new Exception("foo() failed");p.bar(); // now use p

omdat gooien een statement is, geen expressie, moeten uitdrukkingen die dit moeten doen worden opgesplitst in meerdere statements,en extra variabelen worden geïntroduceerd.(Voor een grondige behandeling van deze kwestie, zie de documenten van Andrei Alexandrescu en Petru Marginean).Met luie evaluatie kan dit alles worden ingekapseld in een enkele functie:

Abc Enforce(Abc p, lazy const(char) msg){ if (!p) throw new Exception(msg()); return p;}

en het openingsvoorbeeld hierboven wordt eenvoudig:

Enforce(foo(), "foo() failed").bar();

en 5 regels code worden één. Afdwingen kan worden verbeterd door het atemplate-functie:

T Enforce(T)(T p, lazy const(char) msg){ if (!p) throw new Exception(msg()); return p;}

conclusie

luie evaluatie van Functieargumenten breidt de expressiekracht van functies aanzienlijk uit. Het laat de inkapseling toe in functies van veelvoorkomende codeerpatronen en idiomen die eerder te onhandig ofpraktisch waren om te doen.Dankbetuigingen

ik ben dankbaar voor de inspiratie en hulp van Andrei Alexandrescu, Bartosz Milewski en David Held. De D-Gemeenschap heeft veel geholpen met veel opbouwende kritiek, zoals de rode draad die begon met Tomasz Stachowiak in d/41633.

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.