doven evaluering af Funktionsargumenter

Rapporter en fejl

hvis du ser et problem med denne side, klik her for at oprette et problem.

forbedre denne side

hurtigt gaffel, redigere online, og indsende en pull anmodning om denne side.Kræver en logget ind GitHub konto. Dette fungerer godt til små ændringer.Hvis du gerne vil foretage større ændringer, kan du overveje at brugeen lokal klon.

af Bright, http://www.digitalmars.com/d

Lat evaluering er teknikken til ikke at evaluere et udtrykumindre og indtil resultatet af udtrykket er påkrævet.& & | / / og ?: operatører er den konventionelle måde at gøre doven evaluering på:

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

det andet udtryk p evalueres ikke, medmindre pis ikke null.Hvis det andet udtryk ikke blev dovent evalueret, ville detgenerere en runtime-fejl, hvis p Var null.

mens uvurderlig, de dovne evaluering operatører har betydeligebegrænsninger. Overvej en logging funktion, som logsa besked, og kan slås til og fra ved runtime baseret på en globalvalue:

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

tit, beskedstrengen konstrueres ved kørsel:

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

mens dette virker, problemet er, at opbygningen af messagestring sker uanset om logning er aktiveret eller ej.Med applikationer, der gør stor brug af logning, kan dette bliveet forfærdeligt afløb på ydeevne.

en måde at løse det på er ved at bruge doven evaluering:

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

men dette overtræder indkapslingsprincipper ved at udsætte detaljerne om logning for brugeren. I C, dette problem arbejdes ofte rundtved hjælp af en makro:

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

men det er bare papirer over problemet. Preprocessormakroer harvelkendte mangler:

  • logningsvariablen er eksponeret i brugerens navneområde.
  • makroer er usynlige for symbolske debuggere.
  • makroer er kun globale og kan ikke afgrænses.
  • makroer kan ikke være klassemedlemmer.
  • makroer kan ikke have deres adresse taget, så kan ikke overføres indirekte som funktioner kan.

en robust løsning ville væreen måde at gøre doven evaluering af funktionsparametre. En sådan mådeer muligt I d programmeringssprog ved hjælp af en delegeret 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 bliver strengbygningsudtrykket kun evalueret, hvis loggingis sandt, og indkapsling opretholdes. Det eneste problem er detfå vil ønske at pakke udtryk med { return eksp; }.

så d tager det et lille, men afgørende skridt videre(foreslået af Andrei Aleksandrescu).Ethvert udtrykkan implicit konverteres til en delegeret, der returnerer enten ugyldig ellertypen af udtrykket.Den delegerede erklæring erstattes af den dovne lagringsklasse(foreslået af Tomas.Funktionerne bliver derefter:

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

hvilket er vores oprindelige version, bortset fra at strengen nu ikke erkonstrueret, medmindre logning er tændt.

når som helst der er et gentagende mønster set i kode, at være i stand til at abstract det mønster og indkapsle det betyder, at vi kan reducere kodens kompleksitet og dermed fejl. Det mest almindelige eksempel påDette er funktionenselv.Doven evaluering muliggør indkapsling af en række andre mønstre.

for et simpelt eksempel, antag et udtryk skal evalueres counttimes. Mønsteret er:

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

dette mønster kan indkapsles i en funktion ved hjælp af doven evaluering:

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

det kan bruges som:

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

som vil udskrive:

0123456789

mere komplekse brugerdefinerede kontrolstrukturer er mulige.Her er en metode til at oprette en omskifterlignende 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 bruges 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 udskrive:

it is 2

de, der er bekendt med Lisp programmeringssprog, vil bemærke nogleintriguing paralleller med Lisp makroer.

for et sidste eksempel er der det fælles mønster:

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

fordi kaste er en erklæring, ikke et udtryk, udtryk derskal gøre dette skal opdeles i flere udsagn,og ekstra variabler introduceres.(For en grundig behandling af dette problem, se Andrei Aleksandrescu ogpetru Marginean ‘ s paperenforments).Med doven evaluering, dette kan alle indkapsles i en enkeltfunktion:

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

og åbningseksemplet ovenfor bliver simpelthen:

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

og 5 linjer kode bliver en. Håndhæve kan forbedres ved at gøre det atemplate funktion:

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

konklusion

doven evaluering af Funktionsargumenter udvider dramatisk den ekspressivefunktioner. Det muliggør indkapsling i funktioner af mange almindelige kodningsmønstre og idiomer, der tidligere var for klodset eller praktisk at gøre.

anerkendelser

jeg anerkender taknemmeligt inspiration og hjælp fra Andrei Aleksandrescu, Bartos Milevski og David Held. D-samfundet hjalp meget med meget konstruktiv kritik, som f.eks.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.