Lat utvärdering av Funktionsargument

Rapportera ett fel

om du upptäcker ett problem med den här sidan, Klicka här för att skapa ett Bugzilla-problem.

förbättra den här sidan

snabbt gaffel, redigera på nätet, och skicka en pull begäran om denna sida.Kräver ett inloggat GitHub-konto. Detta fungerar bra för små förändringar.Om du vill göra större ändringar kan du överväga att använda en lokal klon.

följ Walter Bright, http://www.digitalmars.com/d

Lat utvärdering är tekniken att inte utvärdera ett uttryckolöst och tills resultatet av uttrycket krävs.Den & & | / / och ?: operatörer är det konventionella sättet attgör Lat utvärdering:

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

det andra uttrycket p utvärderas inte om inte pi inte är null.Om det andra uttrycket inte utvärderades Lat skulle det generera ett runtime-fel om p var null.

även om de är ovärderliga har de lata utvärderingsoperatörerna betydandebegränsningar. Tänk på en loggningsfunktion, som loggar ett meddelande, och kan slås på och av vid körning baserat på en globalvalue:

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

ofta kommer meddelandesträngen att byggas vid körning:

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

medan detta fungerar är problemet att byggandet av meddelandet sker oavsett om loggning är aktiverad eller inte.Med applikationer som gör stor användning av loggning kan detta bliett hemskt avlopp på prestanda.

ett sätt att fixa det är att använda Lat utvärdering:

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

men detta bryter mot inkapslingsprinciper genom att exponera detailsof loggning till användaren. I C arbetar detta problem ofta runtgenom att använda ett makro:

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

men det bara papper över problemet. Förprocessormakron harväl kända brister:

  • loggningsvariabeln exponeras i användarens namnrymd.
  • makron är osynliga för symboliska felsökare.
  • makron är endast globala och kan inte begränsas.
  • Makron kan inte vara klassmedlemmar.
  • Makron kan inte få sin adress tagen, så kan inte skickas indirekt som funktioner kan.

en robust lösning skulle varaett sätt att göra Lat utvärdering av funktionsparametrar. Ett sådant sättär möjligt i d-programmeringsspråket med hjälp av en delegatparameter:

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 utvärderas strängbyggnadsuttrycket bara om loggingis true, och inkapsling upprätthålls. Det enda problemet är attfå kommer att vilja sätta in uttryck med { return exp;}.

så D tar det ett litet men avgörande steg längre (föreslaget av Andrei Alexandrescu).Varje uttryckkan implicit konverteras till en delegat som returnerar antingen ogiltig eller typen av uttrycket.Delegatdeklarationen ersätts av lazy storage-klassen(föreslagen av Tomasz Stachowiak).Funktionerna blir då:

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

vilket är vår ursprungliga version, förutom att nu är strängen intekonstruerad om inte loggning är påslagen.

när som helst finns det ett upprepande mönster som ses i Kod, att kunna abstrakta ut det mönstret och inkapsla det betyder att vi kan minska kodens komplexitet och därmed buggar. Det vanligaste exemplet påDetta är funktionen själv.Lat utvärdering möjliggör inkapsling av en mängd andra mönster.

för ett enkelt exempel, anta att ett uttryck ska utvärderas counttimes. Mönstret är:

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

detta mönster kan inkapslas i en funktion med Lat utvärdering:

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

den kan användas som:

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

som kommer att skriva ut:

0123456789

mer komplexa användardefinierade kontrollstrukturer är möjliga.Här är en metod för att skapa en switch som struktur:

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; }}

som kan användas som:

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")) );}

som kommer att skriva ut:

it is 2

de som är bekanta med Lisp-programmeringsspråket kommer att märka några spännande paralleller med Lisp-makron.

för ett sista exempel finns det vanliga mönstret:

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

eftersom kasta är ett uttalande, inte ett uttryck,uttryck sombehöver göra detta måste delas upp i flera uttalanden och extra variabler introduceras.(För en grundlig behandling av denna fråga, se Andrei Alexandrescu andPetru Marginean ’ s paperEnforcements).Med Lat utvärdering, detta kan alla inkapslas i en singlefunction:

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

och öppningsexemplet ovan blir helt enkelt:

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

och 5 rader kod blir en. Genomdriva kan förbättras genom att göra det atemplate funktion:

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

slutsats

Lat utvärdering av Funktionsargument utökar dramatiskt det uttrycksfullafunktionens kraft. Det möjliggör inkapsling i funktioner hos mångaVanliga kodningsmönster och idiom som tidigare var för klumpiga eller praktiska att göra.

bekräftelser

jag erkänner tacksamt inspirationen och hjälpen från Andrei Alexandrescu, Bartosz Milewski och David Held. D-gemenskapen hjälpte mycket med mycket konstruktiv kritik, till exempel tråden som började med Tomasz Stachowiak I D/41633.

Lämna ett svar

Din e-postadress kommer inte publiceras.