Lat Evaluering Av Funksjonsargumenter

Rapporter en feil

hvis du oppdager et problem med Denne siden, klikk her for å opprette Et Bugzilla-problem.

Forbedre denne siden

gaffel raskt, rediger på nettet og send inn en pull-forespørsel for denne siden.Krever en pålogget GitHub-konto. Dette fungerer bra for små endringer.Hvis du ønsker å gjøre større endringer kan det være lurt å vurdere usinga lokal klone.

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

Lazy evaluering er teknikken for ikke å evaluere et uttrykkufri og til resultatet av uttrykket er nødvendig.Det & & | / / ja?: operatører er den konvensjonelle måten todo lat evaluering:

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

det andre uttrykket p evalueres ikke med mindre pis ikke er null.Hvis det andre uttrykket ikke ble lazily evaluert, ville detgenerere en kjøretidsfeil hvis p var null.

mens uvurderlig, lat evaluering operatører har betydeligbegrensninger. Vurdere en logging funksjon, som loggen melding, og kan slås av og på under kjøring basert på en globalvalue:

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

ofte vil meldingsstrengen bli konstruert ved kjøretid:

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

Mens dette fungerer, er problemet at byggingen av messagestring skjer uansett om logging er aktivert eller ikke.Med applikasjoner som gjør stor bruk av logging, kan dette blien forferdelig belastning på ytelsen.

En måte å fikse det på er å bruke lat evaluering:

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

men dette bryter innkapslingsprinsippene ved å utsette detaljene for logging til brukeren. I C blir dette problemet ofte jobbet rundtved å bruke en makro:

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

men det bare papirer over problemet. Preprosessor makroer harvelkjente mangler:

  • logging-variabelen vises i brukerens navneområde.
  • Makroer er usynlige for symbolske debuggere.
  • Makroer er bare globale, og Kan ikke omfattes.
  • Makroer kan ikke være klassemedlemmer.
  • Makroer kan ikke ha sin adresse tatt, så kan ikke sendes indirekte som funksjoner kan.

en robust løsning ville væreen måte å gjøre lat evaluering av funksjonsparametere. En slik måteer mulig I d-programmeringsspråket ved hjelp 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); });}

nå blir strengbyggingsuttrykket bare evaluert hvis loggingis true, og innkapsling opprettholdes. Det eneste problemet er thatfew kommer til å ønske å pakke uttrykk med { return exp;}.

Så D tar det et lite, men avgjørende skritt videre (foreslått Av Andrei Alexandrescu).Ethvert uttrykkkan implisitt konverteres til en representant som returnerer enten ugyldig ellertypen av uttrykket.Delegatdeklarasjonen er erstattet av lat lagringsklasse (foreslått Av Tomasz Stachowiak).Funksjonene blir da:

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

som er vår opprinnelige versjon, bortsett fra at nå strengen er notconstructed med mindre logging er slått på.

Når som helst er det et repeterende mønster sett i kode, å kunne trekke ut det mønsteret og innkapsle det betyr at vi kan redusere kodenes kompleksitet og dermed feil. Det vanligste eksempelet pådette er funksjonenseg selv.Lazy evaluering muliggjør innkapsling av en rekke andre mønstre.

for et enkelt eksempel, anta at et uttrykk skal evalueres telletider. Mønsteret er:

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

dette mønsteret kan innkapsles i en funksjon ved hjelp av lat evaluering:

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

den kan brukes som:

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

som vil skrive ut:

0123456789

mer komplekse brukerdefinerte kontrollstrukturer er mulige.Her er en metode for å lage en bryter 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 brukes 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 vil skrive ut:

it is 2

de som er kjent Med Lisp-programmeringsspråket, vil legge merke til noen intense paralleller med Lisp-makroer.

for et siste eksempel er det vanlige mønsteret:

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

fordi kaste er en uttalelse, ikke et uttrykk, uttrykk sommå gjøre dette må brytes opp i flere setninger, og ekstra variabler blir introdusert.(For en grundig behandling av dette problemet, se Andrei Alexandrescu andPetru Marginean ‘ s paperEnforcements).Med lat evaluering, dette kan alle være innkapslet i en singlefunction:

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

og åpningseksemplet ovenfor blir ganske enkelt:

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

og 5 linjer med kode blir en. Håndheve kan forbedres ved å gjøre det atemplate funksjon:

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

Konklusjon

Lazy evaluering av funksjonsargumenter utvider dramatisk uttrykksfullmakt av funksjoner. Det gjør det mulig innkapsling i funksjoner av manycommon koding mønstre og idiomer som tidligere var for klønete orimpractical å gjøre.

Anerkjennelser

jeg anerkjenner takknemlig inspirasjonen Og assistansen Til Andrei Alexandrescu, Bartosz Milewski og David Held. D-fellesskapet hjalp mye med mye konstruktiv kritikk, for eksempel tråden som startet Med Tomasz Stachowiak I D/41633.

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert.