evaluarea Lazy a argumentelor funcției

Raportați o eroare

dacă observați o problemă cu această pagină, faceți clic aici pentru a crea o problemă Bugzilla.

îmbunătățiți această pagină

furcă rapid, editați online și trimiteți o cerere de tragere pentru această pagină.Necesită un cont GitHub conectat. Acest lucru funcționează bine pentru mici schimbări.Dacă doriți să faceți modificări mai mari, poate doriți să luați în considerare utilizarea unei clone locale.

votează Walter Bright, http://www.digitalmars.com/d

evaluarea leneșă este tehnica de a nu evalua o expresieindiferent și până când rezultatul expresiei este necesar.& & | / / și ?: operatorii sunt modul convențional todo evaluare leneș:

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

a doua expresie p nu este evaluată decât dacă pis nu este nul.Dacă a doua expresie nu a fost evaluată leneș, ar generați o eroare de rulare dacă p a fost nulă.

deși neprețuit, operatorii de evaluare leneși au semnificativedeficiențe. Luați în considerare o funcție de logare, care logsa mesaj, și poate fi pornit și oprit în timpul rulării bazat pe un globalvalue:

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

adesea, șirul de mesaje va fi construit în timpul rulării:

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

în timp ce acest lucru funcționează, problema este că construirea messagestring se întâmplă indiferent dacă logare este activată sau nu.Cu aplicații care folosesc intens logarea, acest lucru poate devenio scurgere teribilă a performanței.

o modalitate de a repara este prin utilizarea evaluare leneș:

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

dar acest lucru încalcă principiile încapsulare prin expunerea detailsof logare pentru utilizator. În C, această problemă este adesea lucratăprin utilizarea unui macro:

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

dar asta doar lucrări peste problema. Macrocomenzile preprocesorului audeficiențe bine cunoscute:

  • variabila de logare este expusă în spațiul de nume al utilizatorului.
  • macrocomenzile sunt invizibile pentru depanatoarele simbolice.
  • macrocomenzile sunt doar globale și nu pot fi urmărite.
  • macrocomenzile nu pot fi membri ai clasei.
  • macrocomenzile nu pot avea adresa luată, deci nu pot fi transmise indirect, așa cum pot funcțiile.

o soluție robustă ar fio modalitate de a face evaluarea leneșă a parametrilor funcției. Un astfel de modeste posibil în limbajul de programare D folosind un parametru delegat:

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

acum, expresia string building este evaluată numai dacă loggingul este adevărat și încapsularea este menținută. Singura problemă este astapuțini vor dori să înfășoare expresii cu { return exp;}.

deci D face un pas mic, dar crucial, mai departe(sugerat de Andrei Alexandrescu).Orice expresiepoate fi convertit implicit într-un delegat care returnează fie void sautipul expresiei.Declarația delegatului este înlocuită de clasa lazy storage(sugerată de Tomasz Stachowiak).Funcțiile devin apoi:

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

care este versiunea noastră originală, cu excepția faptului că acum șirul nu esteconstruit decât dacă înregistrarea este activată.

de fiecare dată când există un model repetat văzut în cod, fiind capabil să extragă acel model și să-l încapsuleze, înseamnă că putem reduce complexitatea codului și, prin urmare, erorile. Cel mai comun exemplu alaceasta este funcțiaîn sine.Evaluarea Lazy permite încapsularea unei serii de alte modele.

pentru un exemplu simplu, să presupunem că o expresie urmează să fie evaluată countimes. Modelul este:

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

acest model poate fi încapsulat într-o funcție folosind evaluarea leneșă:

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

poate fi folosit ca:

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

care va imprima:

0123456789

sunt posibile structuri de control mai complexe definite de utilizator.Iată o metodă pentru a crea un comutator ca structura:

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

care poate fi folosit ca:

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

care va imprima:

it is 2

cei familiarizați cu limbajul de programare Lisp vor observa câteva paralele interesante cu macrocomenzile Lisp.

pentru un ultim exemplu, există modelul comun:

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

deoarece aruncarea este o afirmație, nu o expresie,expresii caretrebuie să facă acest lucru trebuie să fie împărțite în mai multe declarații și sunt introduse variabile suplimentare.(Pentru o tratare temeinică a acestei probleme, a se vedea declarațiile de hârtie ale lui Andrei Alexandrescu și Petru Marginean).Cu evaluarea leneșă, toate acestea pot fi încapsulate într – o singură funcție:

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

și exemplul de deschidere de mai sus devine simplu:

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

și 5 linii de cod devin una. Punerea în aplicare poate fi îmbunătățită prin a face funcția atemplate:

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

concluzie

evaluarea leneșă a argumentelor funcției extinde dramatic expresivputerea funcțiilor. Permite încapsularea în funcții a multor modele comune de codificare și idiomuri care anterior erau prea stângace sau nepractice de făcut.

mulțumiri

recunosc cu recunoștință inspirația și asistența lui Andrei Alexandrescu, Bartosz Milewski și David Held. Comunitatea D A ajutat foarte mult cu multe critici constructive, cum ar fi firul care începe cu Tomasz Stachowiak în D/41633.

Lasă un răspuns

Adresa ta de email nu va fi publicată.