X-Git-Url: http://git.polytechnique.org/?a=blobdiff_plain;f=classes%2Fpliteratorutils.php;h=dfa8a36fabd91b016a67f27d0afd2cc1935e82be;hb=5d0c351e87013f73d7176d55814dc650f91a2939;hp=6d995a6ae9ae3706ad92dd711e7a1912297c63b5;hpb=5177a1b50e47e7d62a66aba9565580af3abf15dd;p=platal.git diff --git a/classes/pliteratorutils.php b/classes/pliteratorutils.php index 6d995a6..dfa8a36 100644 --- a/classes/pliteratorutils.php +++ b/classes/pliteratorutils.php @@ -1,6 +1,6 @@ array(0 => key_for_depth0 [, 1 => key_for_depths1, ...]), * value => the value); */ - public static function fromArray(array $array, $depth = 1) + public static function fromArray(array $array, $depth = 1, $flat = false) { - return new PlArrayIterator($array, $depth); + return new PlArrayIterator($array, $depth, $flat); } @@ -59,9 +59,64 @@ class PlIteratorUtils { return new PlMergeIterator($iterators, $callback, $sorted); } -} + /** Build an iterator that contains only the elements of the given iterator that + * match the given condition. The condition should be a callback that takes an element + * and returns a boolean. + * @param iterator The source iterator + * @param callback The condition + * @return an iterator + */ + public static function filter(PlIterator $iterator, $callback) + { + return new PlFilterIterator($iterator, $callback); + } + + + /** Build an iterator that transforms the element of another iterator. The callback + * takes an element and transform it into another one. Be careful: if the result + * of the callback is null, the iteration ends. + * @param iterator The source iterator + * @param callback The transformer. + */ + public static function map(PlIterator $iterator, $callback) + { + return new PlMapIterator($iterator, $callback); + } + + /** Build an iterator whose values are iterators too; such a 'subIterator' will end + * when the value of $callback changes + * @param iterator The source iterator + * @param callback The callback for detecting changes. + * @return an iterator + */ + public static function subIterator(PlIterator $iterator, $callback) + { + return new SubIterator($iterator, $callback); + } + + /** Returns the callback for '$x -> $x[$key]'; + * @param $key the index to retrieve in arrays + * @return a callback + */ + public static function arrayValueCallback($key) + { + $callback = new _GetArrayValueCallback($key); + return array($callback, 'get'); + } + + /** Returns the callback for '$x -> $x->prop'; + * @param $property The property to retrieve + * @return a callback + */ + public static function objectPropertyCallback($property) + { + $callback = new _GetObjectPropertyCallback($property); + return array($callback, 'get'); + } +} + /** Iterates over an array. */ class PlArrayIterator implements PlIterator @@ -75,8 +130,9 @@ class PlArrayIterator implements PlIterator private $_first; private $_last; private $_pos; + private $_flat; - public function __construct(array &$array, $depth = 1) + public function __construct(array &$array, $depth = 1, $flat = false) { $this->array =& $array; $this->depth = $depth; @@ -84,6 +140,7 @@ class PlArrayIterator implements PlIterator $this->_pos = 0; $this->_first = false; $this->_last = false; + $this->_flat = $flat; for ($i = 0 ; $i < $depth ; ++$i) { if ($i == 0) { @@ -146,8 +203,12 @@ class PlArrayIterator implements PlIterator $keys[] = key($this->_its[$i]); } next($this->_its[$this->depth - 1]); - return array('keys' => $keys, - 'value' => $val); + if ($this->_flat) { + return $val; + } else { + return array('keys' => $keys, + 'value' => $val); + } } public function total() @@ -253,5 +314,268 @@ class PlMergeIterator implements PlIterator } } + +class PlFilterIterator implements PlIterator { + private $source; + private $callback; + private $element; + private $start; + + public function __construct(PlIterator $source, $callback) + { + $this->source = $source; + $this->callback = $callback; + $this->start = true; + $this->element = null; + } + + private function fetchNext() + { + do { + $current = $this->source->next(); + if (!$current) { + $this->element = null; + $this->start = false; + return; + } + $res = call_user_func($this->callback, $current); + if ($res) { + $this->element = $current; + return; + } + } while (true); + } + + public function next() + { + if ($this->element && $this->start) { + $this->start = false; + } + $elt = $this->element; + if ($elt) { + $this->fetchNext(); + } + return $elt; + } + + public function total() + { + /* This is an approximation since the correct total + * cannot be computed without fetching all the elements + */ + return $this->source->total(); + } + + public function first() + { + return $this->start; + } + + public function last() + { + return !$this->start && !$this->element; + } +} + + +class PlMapIterator implements PlIterator +{ + private $source; + private $callback; + + public function __construct(PlIterator $source, $callback) + { + $this->source = $source; + $this->callback = $callback; + } + + public function next() + { + $elt = $this->source->next(); + if ($elt) { + return call_user_func($this->callback, $elt); + } else { + return null; + } + } + + public function total() + { + return $this->source->total(); + } + + public function first() + { + return $this->source->first(); + } + + public function last() + { + return $this->source->last(); + } +} + +class PlSubIterator implements PlIterator +{ + private $source; + private $callback; + private $next = null; // The next item, if it has been fetched too early by a subiterator + + public function __construct(PlIterator $source, $callback) + { + $this->source = $source; + $this->callback = $callback; + } + + public function next() + { + if ($this->last()) { + return null; + } else { + return new PlInnerSubIterator($this->source, $this->callback, $this, $this->next); + } + } + + /** This will always be a too big number, but the actual result can't be easily computed + */ + public function total() + { + return $this->source->total(); + } + + public function last() + { + return ($this->source->last() && $this->next == null); + } + + public function first() + { + return $this->source->first(); + } + + // Called by a subiterator to "rewind" the core iterator + public function setNext($item) + { + $this->next = $item; + } +} + +class PlInnerSubIterator implements PlIterator +{ + private $source; + private $callback; + private $parent; + + private $next; // Not null if the source has to be "rewinded" + + private $curval = null; + private $curelt = null; + private $stepped = false; + private $over = false; + + public function __construct(PlIterator $source, $callback, PlSubIterator $parent, $next = null) + { + $this->source = $source; + $this->callback = $callback; + $this->parent = $parent; + $this->next = $next; + } + + public function value() + { + $this->_step(); + return $this->curval; + } + + // Move one step, if the current element has been used + private function _step() + { + if ($this->stepped) { + return; + } + + if ($this->next != null) { + $this->curelt = $this->next; + $this->next = null; + } else { + $elt = $this->source->next(); + } + $this->stepped = true; + } + + public function next() + { + $this->_step(); + $this->stepped = false; + + if ($this->elt) { + $val = call_user_func($this->callback, $this->elt); + if ($val == $this->curval) { + $this->curval = $val; + return $this->elt; + } else { + $this->parent->setNext($this->elt); + } + } + $this->over = true; + return null; + } + + /** This will always be a too big number, but the actual result can't be easily computed + */ + public function total() + { + return $this->source->total(); + } + + public function last() + { + return $this->over; + } + + public function first() + { + return false; + } + +} + +// Wrapper class for 'arrayValueCallback' (get field $key of the given array) +class _GetArrayValueCallback +{ + private $key; + + public function __construct($key) + { + $this->key = $key; + } + + public function get(array $arr) + { + if (array_key_exists($this->key, $arr)) { + return $arr[$this->key]; + } else { + return null; + } + } +} + +// Wrapper class for 'objectPropertyCallback' (get property ->$blah of the given object) +class _GetObjectPropertyCallback +{ + private $property; + + public function __construct($property) + { + $this->property = $property; + } + + public function get($obj) + { + $p = $this->property; + return @$obj->$p; + } +} + // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8: ?>