függvény argumentumok lusta értékelése

Hibabejelentés

Ha problémát észlel ezen az oldalon, kattintson ide a Bugzilla probléma létrehozásához.

javítsa ezt az oldalt

gyorsan villázzon, szerkesszen online, és nyújtson be lekérési kérelmet ehhez az oldalhoz.Aláírt GitHub-fiókot igényel. Ez jól működik kis változások esetén.Ha nagyobb változtatásokat szeretne végrehajtani, érdemes megfontolni a helyi klón használatát.

szerző: Walter Bright, http://www.digitalmars.com/d

a lusta értékelés a kifejezés értékelésének technikájaés amíg a kifejezés eredménye nem szükséges.A & &, / / és ?: az üzemeltetők a szokásos módszertcsinálj lusta értékelést:

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

a második kifejezés p csak akkor értékelhető, ha pis Nem null.Ha a második kifejezés nem volt lustán értékelve, akkor futásidejű hibát generál, ha p null.

bár felbecsülhetetlen, a lusta értékelő operátorok jelentős korlátokkal rendelkeznek. Vegyünk egy naplózási funkciót, amely logsa üzenetet, és be – és kikapcsolható futásidőben egy globalvalue alapján:

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

gyakran előfordul, hogy az üzenet karakterlánc kerül kialakításra futásidőben:

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

amíg ez működik, a probléma az, hogy az üzenet felépítése megtörténik, függetlenül attól, hogy a naplózás engedélyezve van-e vagy sem.Olyan alkalmazásokkal, amelyek nagymértékben használják a naplózást, ez válhatszörnyű teljesítménycsökkenés.

a javítás egyik módja a lusta értékelés használata:

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

de ez sérti a beágyazási elveket azáltal, hogy felfedi a naplózás részleteit a felhasználó számára. A C-ben ezt a problémát gyakran egy makró használatával dolgozzák fel:

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

de ez csak papírokat jelent a probléma felett. Az előfeldolgozó makrók jól ismert hiányosságokkal rendelkeznek:

  • a naplózási változó ki van téve a felhasználó névterében.
  • a makrók láthatatlanok a szimbolikus hibakeresők számára.
  • a makrók csak globálisak, hatókörük nem változtatható meg.
  • a makrók nem lehetnek osztálytagok.
  • a makrók nem tudják felvenni a címüket, ezért nem adhatók át közvetetten, mint a funkciók.

robusztus megoldás lenne a funkcióparaméterek lusta értékelésének módja. Ilyen módlehetséges a D programozási nyelvben egy delegált paraméter használatával:

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

most, a string building kifejezés csak akkor lesz kiértékelve, ha a loggingis true, és a kapszulázás megmarad. Az egyetlen baj az, hogynéhányan a { return exp;} kifejezésekkel akarják becsomagolni a kifejezéseket.

tehát D egy kicsi, de döntő lépést tesz tovább(Andrei Alexandrescu javasolta).Bármely kifejezés implicit módon átalakítható egy delegálttá, amely a void vagy a kifejezés típusát adja vissza.A delegált nyilatkozat helyébe a lusta tárolási osztály lép (Tomasz Stachowiak javasolta).A funkciók ezután válnak:

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

ami az eredeti verziónk, azzal a különbséggel, hogy most a karakterlánc nemstrukturált, kivéve, ha a naplózás be van kapcsolva.

bármikor, amikor egy ismétlődő minta látható a kódban, az, hogy ezt a mintát ki tudjuk bontani és beágyazni, azt jelenti, hogy csökkenthetjük a kód összetettségét, és ezáltal a hibákat. A leggyakoribb példa aez a funkciómagát.A lusta értékelés lehetővé teszi számos más minta beágyazását.

egy egyszerű példa, Tegyük fel, hogy egy kifejezés kell értékelni counttimes. A minta:

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

ez a minta lusta kiértékeléssel beágyazható egy funkcióba:

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

ezt fel lehet használni, mint:

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

amely kinyomtatja:

0123456789

bonyolultabb, felhasználó által definiált vezérlési struktúrák lehetségesek.Itt van egy módszer egy kapcsolóhoz hasonló szerkezet létrehozására:

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

amit úgy lehet használni, mint:

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

amely kinyomtatja:

it is 2

azok, akik ismerik a Lisp programozási nyelvet, észrevesznek néhány érdekes párhuzamot a Lisp makrókkal.

egy utolsó példa, ott van a közös minta:

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

mivel a dobás egy állítás, nem kifejezés, ezért olyan kifejezéseket kell használni,amelyeket több utasításra kell bontani, és további változókat kell bevezetni.(Ennek a kérdésnek az alapos kezelését lásd Andrei Alexandrescu andPetru Marginean ‘ s paperforecements).Lusta értékeléssel, ez mind egyetlen funkcióba ágyazható:

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

és a fenti nyitó példa egyszerűen:

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

és 5 sornyi kód válik eggyé. Érvényesíteni lehet javítani azáltal, hogy atemplate funkció:

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

következtetés

a függvény argumentumok lusta értékelése drámai módon kiterjeszti a kifejezésta funkciók ereje. Lehetővé teszi a sok közös kódolási minta és idióma funkcióinak beágyazását, amelyek korábban túl ügyetlenek vagy gyakorlatiasak voltak.

Köszönetnyilvánítás

hálásan köszönöm Andrei Alexandrescu, Bartosz Milewski és David Held inspirációját és segítségét. A D Közösség sokat segített sok konstruktív kritikával, például a Tomasz Stachowiakkal kezdődő szál A D/41633-ban.

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.