<?php
/***************************************************************************
- * Copyright (C) 2003-2009 Polytechnique.org *
+ * Copyright (C) 2003-2010 Polytechnique.org *
* http://opensource.polytechnique.org/ *
* *
* This program is free software; you can redistribute it and/or modify *
{
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');
+ }
+}
+
/** Iterates over an array.
*/
class PlArrayIterator 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);
+ }
+
+ // 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;
+ }
+
+}
+
+// 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($key, $arr)) {
+ return $arr[$key];
+ } else {
+ return null;
+ }
+ }
+}
+
// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
?>