A PHP programozási nyelv

Alprogramok, modulok

Szintaxis

PHP-ban a függvényeket a function kulcsszóval vezetjük be. A függvények általános szerkezetét a következő szintaxis definiálja:

function fv($arg_1, $arg_2, /* ..., */ $arg_n){ /* some inner activity */ return $retval; }

PHP 4-ig a függvényeket még azelőtt definiálni kellett, mielőtt hivatkozás történt volna rájuk. PHP 4-től már nincs ez a megkötés. Ha egy forrásfájl betöltésre kerül, akkor az értelmező először megkeresi a külső, globális térben lévő függvény definíciókat. Ezután az interpreter a végrehajtás során már értelmezni tudja a forráskódban hátrébb lévő függvényeket is.

A függvénynevek nem érzékenyek a kis- és nagybetűkre, de az elfogadott gyakorlat az hogy olyan formában hívjuk meg, mint ahogyan deklaráltuk.

Visszaadott érték

PHP-ban minden alprogram függvény. Ha a kódunk nem ad vissza a return kulcsszóval értéket, azaz egy eljárást írtunk, akkor is explicit módon egy alapértelmezett NULL értékkel tér vissza a függvényünk. A függvényeknek – jelenleg a PHP 5 verziónál – nem lehet megadni, hogy milyen típusú értékkel térjenek vissza. Arra viszont lehetőség van hogy a függvény belsejében lévő végrehajtási útvonalak végén más-más típusú változót adjunk vissza.

Az alábbi kód egyes lefutás esetén nem ad vissza értéket (az explicit NULL-on kívül), más lefutás esetén viszont van visszatérési értéke. Amikor van visszatérési értéke, akkor az String vagy Integer típusú lehet.

function fact($n) { if (!is_int($n)) return "ne bolondozz!"; if ($n>=0) { if ($n>1) return $n*fact($n-1); else return 1; } } echo fact(3); echo fact(-1); echo fact("szevasz");

A program futásának eredménye:

6 NULL ne bolondozz!

Visszatérési érték típusára nem lehet korlátozást megadni, viszont a nyelv 6.0-ás megjelenésével az ígéretek szerint erre lesz lehetőség, de valószínűleg csak egyes típusokra (array, osztály vagy leszármazottja és interface megvalósítása).

Rekurzió

Mint látható a fenti példában, a PHP támogatja a rekurzív függvények hívását. Azonban - a php.net álláspontja szerint – kerülni kell a több mint 100-200 mélységű rekurzív hívást, mert ez felemésztheti a hívásivermet, így a szkript váratlanul befejezheti a futását.

Feltételes definiálás

Lehetőség van arra, hogy logikai értékektől függően definiáljunk egyes alprogramokat vagy sem. A következő példában a $valaszt tartalmától függően kerül definiálásra a melyik() függvény egyes variánsai.

$valaszt = false; fv(); melyik(); //Fatal error: Call to undefined function if ($valaszt) { function melyik() { echo "Én hívódok meg.\n"; } } else { function melyik() { echo "Nem igaz! Én hívódok meg!!!.\n"; } } melyik(); function fv() { echo "PHP 4.0 óta én már az induláskor létezek .\n"; }

Dinamikus és névtelen függvények

A függvényekhez rendelhetőek változók, melyekkel később el tudjuk érni az adott függvényt. Az így elért (meghívott) függvényeket nevezzük dinamikus függvényeknek. Ígéretek szerint erre a lehetőségre később – a PHP 6 megjelenésétől – nem lesz már lehetőség.
A dinamikus függvényekre mutat példát a következő kódrészlet:

function kisebb($a, $b) { return $a<$b; } $fv = "kisebb"; echo $fv(1,2); $fv = "nagyobb"; $fv(1,2); //nagyobb hívása

Látszik a fenti kódnál, illetve már a korábban bemutatott feltételesen definiált függvényeknél is hogy a függvények definiálását nagyon dinamikusan lehet kezelni PHP-ban és egy idő után – illetve nagyobb projekteknél – átláthatatlanná válhat az ilyen módon létrehozott függvények kezelése. Ezért a PHP biztosít beépített függvényeket, mellyel ellenőrizni tudjuk, az adott függvény meglétét.

if (function_exists($fv)) { echo $fv(1,3); }

Lehetőségünk van a create_function() függvénnyel további, úgynevezett névtelen függvény létrehozására. Két paramétert vár a függvénylétrehozó. Az első paraméter a létrehozandó függvény paraméteri a második pedig maga a törzs. Mindkét paramétert String formájában adjuk meg, és ügyelni kell az aposztróf használatára, hogy a létrehozás pillanatában ne értékelődjenek ki. Ily módon egy olyan kényelmes és erős eszközt kapunk, mellyel futásidőben tudunk tetszőleges függvényeket létrehozni.

$nevtelen = create_function('$a, $b', 'return $a+$b;'); echo $nevtelen(4, 5);

A PHP 5.3. verziójától létrehozhatók úgynevezett closure-ök, amelyek a create_function() eszköznél természetesebb szintaktikával valósítják meg a névtelen függvényeket. Elsősorban callback paraméterként használják, de változóknak is értékül adhatók, illetve sok egyéb alkalmazási módjuk is van.

Closure deklarálásakor egyszerűen elhagyjuk a függvény nevét. Ha egy változónak adjuk értékül, ugyanúgy használjuk, mint bármely értéket, a sorvégi pontosvesszőt is ki kell tennünk.

$greet = function($name) { printf("Hello %s\r\n", $name); }; $greet('World');

A closure – a függvénynév elhagyásán és a dinamikus tulajdonságon kívül – abban különbözik a hagyományos függvényektől, hogy megörökölhet változókat a szülő scope-jából, ahol a szülő az a blokk, amelyben a closure-t deklarálták (és nem feltétlenül az, amelyből meghívták). Ez lényegesen különbözik a globális változók használatától! Változók átörökítéséhez a use kulcsszót használjuk, amely után zárójelben soroljuk fel azokat a változókat, amelyeket használni fogunk a closure törzsében.

$kulso = "kezdeti ertek"; $closure = function() use ($kulso) { echo $kulso; }; $kulso = "modositott ertek"; $closure();

A fenti példában a program kiírja a képernyőre, hogy „kezdeti ertek”. A use záradékra úgy is tekinthetünk, mint egy rejtett paraméterlistára, amelynek aktuális paraméterei mindig a deklaráció pillanatában érvényes értékek. Ennek megfelelően itt is használhatunk referencia szerinti értékadást, ld. alább:

$kulso = "kezdeti ertek"; $closure = function() use (&$kulso) { echo $kulso; }; $kulso = "modositott ertek"; $closure();

Ekkor a kiírt érték már a „modositott ertek” lesz, mivel a $kulso változót referencia szerint adtuk át. Értéke a closure törzsében módosítható is.

A closure kifejezéseket a nyelv automatikusan a beépített Closure osztály példányaivá konvertálja. Tehát ezek a névtelen függvények valójában objektumok. Kezdetben ezt a megoldást csupán az implementáció mellékes részletének tekintették, amely nem tartozik a fejlesztőkre, PHP 5.4-től azonban az osztály metódusokkal is rendelkezik, valamint a $this változó is elérhető a closure törzsében. Ez pedig további eszközöket ad a programozó kezébe, lehetővé teszi például, hogy egy objektumot futás közben további metódusokkal bővítsünk.
Az erre vonatkozó példát lásd: http://www.php.net/manual/en/closure.bind.php

A Closure osztályt kézileg példányosítani és belőle származtatni nem lehet. Metódusai:

Függvények és környezetük

Globális függvények

Minden függvény és osztály láthatósága globális, azaz bárhonnan meghívható, amennyiben definiálásra kerültek.

Bármely érvényes PHP kód megjelenhet egy függvényen belül, akár még más függvény vagy osztály definíciók is.

A következő kód egy példát mutat a globális függvényekre:

function kul_fv() { function bel_fv() { echo "Engem ne hívj, amíg kul_fv()-t nem hívtad!\n"; } } bel_fv(); //Fatal error: Call to undefined function bel_fv() kul_fv(); bel_fv(); kul_fv(); //Fatal error: Cannot redeclare bel_fv()

A bel_fv() első meghívására végzetes hiba fog keletkezni, mivel az adott függvény még nem került definiálásra. A kul_fv() már a file beolvasásakor definiálásra került, így nem is ad hibát a függvény első meghívása. Lefutása után a bel_fv() is definiálásra kerül, így a függvény meghívása esetén már hiba nélkül fut le. A PHP-ban nincs lehetőség a megszokott függvénytúlterhelésre, így ha még egyszer meg szeretnénk hívni a külső függvényt, akkor végzetes hiba keletkezik, mivel a bel_fv() már definiálásra került. Ezt egy blokkszerkezetű nyelvhez szokott programozó furcsának találhatja, azonban alkalmazása hasznos lehet, ha függvények különböző megvalósításit (variánsait) szeretnénk betölteni egy-egy függvényhívással.

Globális változók elérése

A változók lokális hatókörűek, azaz a függvényeken kívüli változók alapértelmezetten nem elérhetőek. Elérésüket a $GLOBALS globális tömbbel vagy a global kulcsszóval lehet biztosítani.

$a = 1; $b = 2; function sum1() { $b = $a + $b; } function sum2() { global $a, $b; $b = $b + $a; } function sum3() { $GLOBALS['b'] = $GLOBALS['a']+$GLOBALS['b']; } sum1(); echo $b; sum2(); echo $b; sum3(); echo $b;

A program futása a következő kimenetet eredményezi:

2 PHP Notice: Undefined variable:b 3 4

Várhatólag a PHP 6.0 érkezésével a $GLOBALS tömb elérése deprecated lesz.

Paraméterátadás

A bemenő paraméter típusára csak minimális megszorítást tehetünk. Jelölhetjük, hogy az adott paraméter egy tömb, egy osztály és annak leszármazottjának példánya, vagy egy interface és annak leszármazottjának megvalósítása. Érezhető hogy ennek a lehetőségnek a kiaknázására igazából csak OO elemek használata esetén van lehetőség.

A PHP támogatja az érték szerinti és a referencia szerinti paraméterátadást is. Alapértelmezésben a függvény paraméterei érték szerint adódnak át (vagyis ha megváltozik a változó értéke a függvényen belül, annak nincs hatása a függvényen kívüli környezetre).

Ha azt szeretnénk megengedni, hogy a függvény módosítsa az átadott paramétereket, akkor referencia szerint kell átadni azokat. Ezt a függvénydefinícióban az argumentum elé elhelyezett & jelel tudjuk elérni:

function megbabol(&$szo) { $szo .= 'bab'; } $str = ’Hagymás'; megbabol($str); echo $str;

Korábbi PHP verziókban lehetőség volt arra, hogyha a függvénydefiníció argumentuma előtt semmilyen a paraméterátadásra módjára vonatkozó jelzést nem tettünk, akkor hívó oldalról dönthettünk a paraméterátadás módjáról. Azaz a fenti példában ha megbabol($str) függvényhívást használtuk, akkor érték szerint adódott át, míg ha a megbabol(&$str) függvényhívás esetén pedig referencia szerinti paraméterátadás történik. Az újabb verziókban ez a lehetőség már depricated-é vált.

A referencia szerinti paraméterátadás nagyobb objektumok, tömbök esetén jelentősen gyorsabb mint az érték szerinti. Korábban a jobb teljesítmény elérése céljából nagy figyelmet kellett erre fordítani a paraméterátadás módjának, viszont PHP 5.0 bevezetésével megjelent a copy on write technika, melynek lényege, hogy az érték szerint átadott paraméter nem másolódik le a hívás pillanatában. A másolás lusta módon történik, csak abban az esetben, ha módosításra, írásra kerülne sor.

A Visszaadott értéknél is lehetőségünk van jelölni az átadás módját. Hasonlóan a bemenő argumentumnál látott módon a & jel segítségével, melyet a függvény neve elé helyezünk el a definiáláskor. A következő kód a cím szerinti paraméterátadásra mutat példát:

function &getName() { $name = "Aladár"; return $name; } $name =& getName();

A hívás helyén szokás jelezni ezt a =& jelel, de használata csak jelzésértékű, így elhagyható. A =& tulajdonképpen két operátor, de a gyakorlatban általában közvetlenül egymás után szokták írni őket.

Argumentumok kezdőértékei

A PHP támogatja az argumentumok kezdőértékének megadásának lehetőségét. A kezdőértéknek konstans kifejezésnek kell lennie, így nem lehet változó, objektum vagy függvényhívás. Bármely függvény skalár-argumentumainak lehetőség van kezdőérték megadására hasonlóan a C++ szintaxisnak megfelelően:

function kavet_csinal($tipus = "cappucino") { return "Csinálok egy pohár " . $tipus . "t.\n"; } echo kavet_csinal(); echo kavet_csinal("espresso");

A fenti kód kimenete:

Csinálok egy pohár cappucinot. Csinálok egy pohár espressot.

A PHP azt is megengedi, hogy tömböt vagy a speciális NULL típust használjunk alapértelmezett értékként:

function kavet_foz($tipusok = array("cappuccino"), $kaveFozo = NULL){ $eszkoz = is_null($kaveFozo) ? "kézzel" : $kaveFozo; return "Főzök egy csésze ".join(", ", $tipusok)." $eszköz.\n"; } echo kavet_foz(); echo kavet_foz(array("cappuccino", "lavazza"), "teafőzővel");

Amennyiben alapértelmezett kezdőértéket adunk, akkor a hívási oldalon az adatot paraméter opcionálissá válik. Nyilvánvalóan az opcionális paramétereket hátra, míg a kötelezően megadandókat előre érdemes tenni az argumentumok sorrendjének megadásakor. Ellenkező esetben – mint a lenti példa mutatja – a fordító nem tud mit kezdeni a nem túl szerencsésen kialakult helyzettel.

Az alapértelmezett függvényargumentumok helytelen használata:

function joghurtot_keszit($type = "acidophilus", $flavour){ return "Készítek egy köcsög $flavour ízű $type-t.\n"; } echo joghurtot_keszit("eper"); // nem úgy működik, mint szeretnéd ?

A fenti példa kimenete:

Warning: Missing argument 2 in call to joghurtot_keszit() in /usr/local/etc/httpd/htdocs/phptest/functest.php on line 41 Készítek egy köcsög ízű eper-t.

Egy trükköt alkalmazva eltekinthetünk a fenti kisebb kényelmetlenségektől. Az argumentumokat adjuk át egy asszociatív tömbben. Az alapértékeket pedig egy másik tömbben adjuk meg a függvény definíciójánál. Ezután menjünk végig az alapértékeket tartalmazó tömb elemein. Ha nem definiálták az adott elemet az argumentumok tömbjében, akkor egyszerűen adjuk hozzá azt mint új elem. Így bármelyik argumentum elhagyható, sőt még annyit is nyerünk a dolgon, hogy nem kell megjegyeznünk a paraméterek sorrendjét.

A PHP 5-ös változatától kezdődően a referenciaként átadott argumentumokat is el lehet látni kezdőértékkel.

Túlterhelés

Korábban láthattuk, hogy a beépített függvényeknél, operátoroknál lehetőség van polimorfizmusra a nyelvben, mint ahogy a lenti is kód mutatja.

echo 1 + 2; echo 1.0 + 2.0; $merged = array(0 => 1) + array(1 => 2); print_r($merged);

A program kimenete:

3 3 ( [0] => 1 [1] => 2 )

Azonban a PHP sajnos nem támogatja a felhasználói függvények polimorfizmusát (többalakúságát). A függvénydefiníciókat nem lehet megszüntetni vagy már definiált függvényeket újradefiniálni. Ez részben arra vezethető vissza, hogy a PHP egy interpretált és gyengén típusos nyelv.

Azonban szimulálhatjuk a függvények polimorfizmusát, amit ha a megvalósítási oldalon (kicsit körülményesen), számos feltétellel és ellenőrzéssel megoldunk, akkor a hívó oldalon már gond nélkül használhatóak polimorf módon. Ezt a lehetőséget biztosítják a változó hosszúságú argumentumlistával rendelkező függvények.

Változó hosszúságú argumentumlista

Változó hosszúságú paraméterlista megadására és felhasználására a PHP két megoldást biztosít. Nézzük előbb a régi és egyben alternatív megoldást.

A PHP 4 és a későbbi változatok támogatják a változó számosságú argumentumlistát a felhasználók által definiált függvényekben. A függvény definíciójában ha üresen hagyjuk az argumentumokat, akkor a függvény hívásakor tetszőleges számú paraméterrel meghívhatjuk az adott függvényt. Hogy ez a módszer működjön, a függvény belsejében számos – általában bonyolult – ellenőrző feltételt kell beépítenünk, melyeket a következő függvények támogatnak:

A következő példa egy tetszőleges számú paraméterben kapott számokat összeadni tudó függvény megvalósítása:

function sum() { $s=0; foreach(func_get_args() as $a) $s+= is_numeric($a)?$a:0; return $s; }; echo sum(1,2,3,4,5,6); echo sum(3,2,1); echo sum(false,array(),5,5);

A program kimenete:

21 6 10

PHP 5.6-tól az argumentumok listája tartalmazhatja a ... tokent, annak jelzéséül az interpreter számára, hogy a függvény változó számosságú változót fogadjon el. Ekkor a szóban forgó formális paraméterek a függvény törzsében, mint egy tömbként érhetőek el.

function sum(...$numbers) { $acc = 0; foreach ($numbers as $n) { $acc += $n; } return $acc; } echo sum(1, 2, 3, 4);

A függvény hívásának eredménye:

10

Továbbá a ... token használható arra is, hogy kicsomagoljunk tömböket vagy Traversable változókat a paraméterlistában:

function add($a, $b) { return $a + $b; } echo add(...[1, 2])."\n"; $a = [1, 2]; echo add(...$a);

A függvény hívásának eredménye:

3 3

Ha normálisan pozícionált paramétereket szeretnénk megadni a függvény szignatúrájában még a ... token előtt, akkor megtehetjük a megszokott módon, a következő példában látottak szerint. Ekkor azok a paraméterek kerülnek a függvény lokális, változó paraméterlistáját reprezentáló tömbbe, amelyek nem kaptak normálisan paraméterezett formális változót.
Megjegyzendő, hogy ... token használatakor is élhetünk a PHP type hint képességeivel.

function total_intervals($unit, DateInterval ...$intervals) { $time = 0; foreach ($intervals as $interval) { $time += $interval->$unit; } return $time; } $a = new DateInterval('P1D'); $b = new DateInterval('P2D'); echo total_intervals('d', $a, $b).' days'; // This will fail, since null isn't a DateInterval object. echo total_intervals('d', null);

A függvény hívásának eredménye:

3 days Catchable fatal error: Argument 2 passed to total_intervals() must be an instance of DateInterval, null given, called in - on line 14 and defined in - on line 2

Generátorok

A generátorok PHP 5.5.0 óta egyszerű és hatékony megoldás kínálnak olyan bejárók definiálására, amelyeket eleddig osztályok segítségével valósítottunk meg - olyan osztályok segítségével, amelyek az Iterator interfészt implementálták. A generátor előnye, hogy mentesülünk attól a költségtől, hogy egy osztály készítsünk, továbbá, hogy egy egész tömböt építsünk a memóriába.
A generátorok segítségével foreach típusú vezérlési szerkezetekkel bejárható adatszerkezeteket készíthetünk anélkül, hogy tömböt inicializálnánk, amely esetlegesen konfliktusban kerülhet a környezet vagy interpreter memória-korlátaival, vagy túl nagy löket-jellegű feldolgozási időt venne igénybe az elkészítése.
A fenti problémák kiküszöbölése érdekében írhatunk egy generátor függvényt, mely nem különbözik a normál függvényektől, csupán abban, hogy minden hívás esetén megtartja az állapotát. A generátor minden hívások egy új eredményt "termel" az állapotának megtartásával.

Megjegyzendő, hogy ilyen generátorok készítésére eddig is volt lehetőség, ugyanis a PHP lehetőséget ad statikus változók létrehozására függvény hatáskörén belül, mely minden új híváskor is megtartja állapotát. Több esetben így szimulálható a generátor működése, de a most bemutatott eszköz gyorsabb és elegánsabb megoldást nyújt

A generátorra egy példa lehet a range függvény implementálása. Az általános range függvénynek egy olyan tömböt kell generálnia, amely tartalmaz minden értéket, mondjuk 0 és 1000000 között. Ennyi integer eltárolására több mint 100 MB-ra van szükség, amely könnyen katasztrófát jelenthet egy webes környezetben, ahol másodpercenként - mondjuk - több ezer felhasználót kell kiszolgálni.
Alternatívaként, ahogy fentebb is említettük, implementálhatunk egy olyan osztályt, amely a bejáró minta szellemében generálja számunkra az értékeket, vagy használhatunk egyszerűen generátort.

A következő példa egy ilyen, alternatív generátort mutat be.

function xrange($start, $limit, $step = 1) { if ($start < $limit) { if ($step <= 0) { throw new LogicException('Step must be +ve'); } for ($i = $start; $i <= $limit; $i += $step) { yield $i; } } else { if ($step >= 0) { throw new LogicException('Step must be -ve'); } for ($i = $start; $i >= $limit; $i += $step) { yield $i; } } } echo 'Single digit odd numbers from range(): '; foreach (range(1, 9, 2) as $number) { echo "$number "; } echo "\n"; echo 'Single digit odd numbers from xrange(): '; foreach (xrange(1, 9, 2) as $number) { echo "$number "; }

A szkript kimenete:

Single digit odd numbers from range(): 1 3 5 7 9 Single digit odd numbers from xrange(): 1 3 5 7 9

Yield

Nézzük a részleteket!
Ahogy említettük, a generátor egy egyszerű függvény. Amikor hívásra kerül, egy olyan objektummal tér vissza, amely iterálható - például - a foreach vezérlési szerkezet használatával. Ekkor az interpreter meghívja a generátor függvényünket minden esetben, amikor újabb értékre van szüksége, továbbá elmenti a generátor állapotát minden hívás után.
Amikor a generátor már nem állít elő újabb értékeket, akkor egyszerűen kiléphet és a hívó kód úgy folytatódhat, mintha egy tömböt kezelne, amelynek a végére érkezett.
A generátornak nincs visszatérési értéke! Csak üres return kifejezés használható! Ha a generátor visszatérésének van értéke, akkor fordítási idejű hiba keletkezik!

A generátorfüggvény alapköve az úgynevezett yield kulcsszó. Formailag egy yield kifejezés éppen úgy néz ki, mint egy return kifejezés. Abban különbözik, hogy a yield szünetelteti a függvény működését visszatérés után, ahelyett, hogy kilépne!
Megjegyzendő, hogy a yield értékeihez az interpreter szekvenciális integer kulcsokat rendel hozzá, ahogy azt a nem-asszociatív tömbök esetén is teszi.

Ha a yield egy kifejezés része - például értékadás jobb oldalán helyezkedik el -, akkor a yield-et tartalmazó kifejezést zárójelbe kell tenni, egyébként fordítási hibát kapunk.

$data = (yield $value);

Mivel a PHP asszociatív tömböket is támogat, így a generátorok számára biztosították a kulcs-érték párral való visszatérést a függvény tervezésekor.
A fenti példával analóg módon, helyesen így adható át kulcs-érték pár a yield használatával.

$data = (yield $key => $value);

Megjegyzés: Ha a yield-nek nincs visszatérési értéke, akkor NULL-lal tér vissza.
Továbbá yield visszatérhez referenciával is, a függvények hasonló módon az & kulcsszó használatával.

Fibonacci

Következzen egy példa Fibonacci sorozat gyártására generátorfüggvény segítéségével:

function getFibonacci() { $i = 0; $k = 1; //first fibonacci value yield $k; while(true) { $k = $i + $k; $i = $k - $i; yield $k; } } $y = 0; foreach(getFibonacci() as $fibonacci) { echo $fibonacci . "\n"; $y++; if($y > 30) { break; // infinite loop prevent } }

Helyes lezárás

A következő példa arra hívja fel a figyelmet, hogy yield-ek használatánál könnyen erőforrás-pazarlásba ütközhetünk egyes esetekben.
A generátor lezárásakor a fenntartott végrehajtási vermet kiüríti - természetesen a változókkal együtt, melyeket felszabadít! Ekkor hamissal tér vissza és a kulcs és változó is NULL lesz. Generátor két féle képpen lehet lezárni:

Ha a generátor tartalmaz releváns finally blokkot, akkor az kerül futtatásra lezáráskor.

function getLines($file) { $f = fopen($file, 'r'); try { while ($line = fgets($f)) { yield $line; } } finally { fclose($f); } } foreach (getLines("file.txt") as $n => $line) { if ($n > 5) break; echo $line; }

Performancia

Performancia tekintetében a generátor 4-szer gyorsabb, mint egy Iterator implementáció és 40%-kal gyorsabb a natív range implementációknál.
Kisebb intervallumokra - például pusztán több száz elem esetén - a generátorok egy hajszállal lassabbak a natív megvalósításokhoz képest.
A fent említett range memória problémára egy mérési eredmény:

---------------------------------- | time | memory, mb | ---------------------------------- | not gen | 0.7589 | 146.75 | |--------------------------------- | with gen | 0.7469 | 8.75 | |---------------------------------