Funktioargumenttien laiska arviointi

ilmoita viasta

jos havaitset vian tällä sivulla, Luo Bugzilla ongelma napsauttamalla tätä.

paranna tätä sivua

nopeasti haarukoi, muokkaa verkossa ja lähetä tälle sivulle pull-pyyntö.Vaatii kirjatun GitHub-tilin. Tämä toimii hyvin pienissä muutoksissa.Jos haluat tehdä suurempia muutoksia, voit harkita paikallisen kloonin käyttöä.

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

laiska arviointi on tekniikka, jossa ekspressiota ei arvioida ilman ja kunnes ilmaisun tulosta vaaditaan.&&, | /ja?: operaattorit ovat tavanomainen tapa onto laiska arviointi:

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

toista lauseketta p ei arvioida, ellei pis ole null.Jos toista lauseketta ei arvioitaisi laiskasti, se aiheuttaisi ajonaikaisen vian, jos p olisi nolla.

vaikka laiskat arviointitoimijat ovat korvaamattomia, heillä on huomattavia rajoituksia. Harkitse lokitoiminto, joka logsa viesti, ja voidaan kytkeä päälle ja pois ajon aikana perustuu globalvalue:

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

usein viestijono rakennetaan suorituksen aikana:

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

vaikka tämä toimii, ongelmana on, että messagestringin rakentaminen tapahtuu riippumatta siitä, onko hakkuu käytössä vai ei.Sovellukset, jotka käyttävät paljon puunkorjuuta, tämä voi ollahuolimaton lasku suorituskykyä.

yksi tapa korjata se on laiskan arvioinnin avulla:

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

mutta tämä rikkoo kapselointiperiaatteita paljastamalla käyttäjälle kirjautumisen yksityiskohdat. C: ssä tätä ongelmaa työstetään usein makron avulla:

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

mutta se vain papereita yli ongelman. Esiprosessorin makroissa on tunnettuja puutteita:

  • kirjausmuuttuja paljastuu käyttäjän nimiavaruudessa.
  • makrot ovat näkymättömiä symbolisille debuggaajille.
  • makrot ovat vain maailmanlaajuisia, eikä niitä voi scopata.
  • makrot eivät voi olla luokan jäseniä.
  • Makroilta ei voi ottaa osoitetta, joten niitä ei voi välittää epäsuorasti samalla tavalla kuin funktioita.

vankka ratkaisu olisi tapa tehdä laiska funktioparametrien arviointi. Tällainen tapa on mahdollinen d-ohjelmointikielellä delegoidun parametrin avulla:

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

nyt, merkkijono rakennus lauseke arvioidaan vain, jos loggingis totta, ja kapselointi säilyy. Ainoa ongelma on, että ne haluavat kääriä lausekkeet { return exp;}.

So D vie sen yhden pienen, mutta ratkaisevan askeleen pidemmälle (Andrei Alexandrescun ehdottama).Mikä tahansa ekspressio voidaan implisiittisesti muuntaa delegaatiksi, joka palauttaa joko mitättömän tai lausekkeen tyypin.Delegaatin ilmoitus korvataan laiskalla tallennusluokalla (ehdotti Tomasz Stachowiak).Funktiot sitten tulevat:

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

joka on meidän alkuperäinen versio, paitsi että nyt merkkijono ei ole structured ellei loki on päällä.

joka kerta kun koodissa nähdään toistuva kuvio, se, että voimme hahmottaa tämän kuvion ja kapseloida sen, tarkoittaa, että voimme vähentää koodin monimutkaisuutta ja siten bugeja. Yleisin esimerkki tästä on funktio itse.Laiska arviointi mahdollistaa kapseloinnin monia muita kuvioita.

yksinkertaisessa esimerkissä oletetaan, että jokin lauseke on arvioitava laskuajat. Kaava on:

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

Tämä kuvio voidaan kapseloida funktioon laiskan arvioinnin avulla:

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

sitä voidaan käyttää kuten:

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

joka tulostaa:

0123456789

monimutkaisemmat käyttäjän määrittämät ohjausrakenteet ovat mahdollisia.Tässä on menetelmä luoda Kytkimen kaltainen rakenne:

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

jota voidaan käyttää kuten:

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

joka tulostaa:

it is 2

Lisp – ohjelmointikielen tuntevat huomaavat jonkin verran rinnastuksia Lisp-makroihin.

viimeisessä esimerkissä on yleinen kaava:

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

koska heitto on lauseke, ei lauseke,lausekkeet, jotka tarvitsevat tätä, on jaettava useiksi lausekkeiksi, ja lisämuuttujia otetaan käyttöön.(Tämän kysymyksen perusteellinen käsittely on Andrei Alexandrescun japetru Marmean ’ s paperenforjoupsin luettavissa).Laiska arviointi, tämä kaikki voidaan kapseloida yhdeksi toiminnoksi:

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

ja avausesimerkki edellä tulee yksinkertaisesti:

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

ja 5 riviä koodia tulee yksi. Valvoa voidaan parantaa tekemällä atemplate toiminto:

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

johtopäätös

Funktioväitteiden laiska arviointi laajentaa dramaattisesti funktioiden ekspressiivistä tehoa. Se mahdollistaa kapseloinnin monien yhteisten koodausmallien ja idiomien funktioihin, jotka aiemmin olivat liian kömpelöitä tai käytännön asioita.

kiitokset

tunnustan kiitollisena Andrei Alexandrescun, Bartosz Milewskin ja David Heldin inspiraation ja avun. D-yhteisö auttoi paljon paljon rakentavalla kritiikillä, kuten Tomasz Stachowiakista alkavalla langalla d/41633: ssa.

Vastaa

Sähköpostiosoitettasi ei julkaista.