Generatory w PHP

Dzisiaj kolejny post z nieoficjalnej serii „zaprzyjaźnij się z PHP” 😛 a dokładnie z php 5.3. Opiszę w nim jak zbudować generator. Czym jest generator ? Jak wyjaśnia nam Wikipedia generator jest rodzajem Iteratora, który zamiast zwracać wygenerowaną wcześniej zawartość oblicza nam ją wraz z kolejnym przebiegiem, dzięki czemu nie musimy od razu pobierać np. całej zawartości jakiejś kolekcji.

Nasz mały generator będzie implementował interfejs „Iterator” i pobierał w konstruktorze jako swój argument dowolną anonimową funkcję, która będzie generować kolejny wynik.
Kod:

class Generator implements Iterator {

    protected $_function;
    protected $_maxN;
    protected $_n = 0;

    public function __construct($function, $maxN) {
        $this->_function = $function;
        $this->_maxN = $maxN;
    }

    public function current() {
        $fFunc = $this->_function;
        return $fFunc($this->_n);
    }

    public function key() {
        return $this->_n;
    }

    public function next() {
        $this->_n++;
    }

    public function rewind() {
        $this->_n = 0;
    }

    public function valid() {
        if ($this->_n < $this->_maxN) {
            return true;
        }
        return false;
    }
}

Nasz generator użyjemy do wygenerowania pierwszych n elementów ciągu fibonnaciego. Najpierw funkcja:

$cFib = function($n) use (&$cFib) {
            if ($n == 0) {
                return 0;
            }
            if ($n == 1) {
                return 1;
            }
            return $cFib($n - 1) + $cFib($n - 2);
        };

Jak widzimy jest to klasyczna implementacja funkcji obliczającej kolejny element ciągu fibonacciego. Z rzeczy które warto zauważyć – jest to domknięcie (closure) i aby funkcja mogła się rekurencyjnie wywołać musi dostać referencje do samej siebie (trochę jest to hak ale inaczej się nie da, __FUNCTION__ nie działa tak jak powinno).

Zastosowanie:

$oGenerator = new Generator($cFib, 5);
foreach($oGenerator as $n => $value){
    echo $n, ' ', $value, '<br/>';
}

Wynik:

0 0
1 1
2 1
3 2
4 3

Koniec końców uzyskaliśmy wygodną klasę do generowania kolejnych wartości. Przy odrobinie większym wysiłku, można by do implementować jeszcze interfejs ArrayAccess i mieć już całkowitą iluzję obcowania z tablicą –

echo $oGenerator[4];
//zwroci 3
  1. cojack pisze:

    Wojciech, szerzej opiszę tą funkcjonalność na swoim blogu bo to jest fajna rzecz. Jak skończę to się odezwę.

  2. cojack pisze:

    A mam to zaimplementowane w swoim FW jako mapę.

  3. spawnm pisze:

    cojack – czyli jednak to co piszesz to framework 😀

  4. Wojciech Soczyński pisze:

    @cojack – cieszę się, że podchwyciłeś temat, czekam na kreatywne rozwinięcie tematu 😉

  5. cojack pisze:

    Ty no nie czepiaj się, już mi wookieb nawytykał. Także nie będę się dłużej z tym bronił, no niech Wam będzie.

  1. There are no trackbacks for this post yet.

Leave a Reply

Informuj mnie o odpowiedziach poprzez e-mail. Możesz również subskrybować wpis bez zostawiania komentarza.