Skip to content

Instantly share code, notes, and snippets.

@mindplay-dk
Last active December 18, 2015 09:18
Show Gist options
  • Select an option

  • Save mindplay-dk/5759952 to your computer and use it in GitHub Desktop.

Select an option

Save mindplay-dk/5759952 to your computer and use it in GitHub Desktop.
An Iterator that computes it's values in a lazy way

LazyIterator

If you never iterate over the full set of values, it never computes them, and it caches the computed values.

Output from "test.php":

computing value for 0
first iteration: 0 => 0
computing value for 1
first iteration: 1 => 1
computing value for 2
first iteration: 2 => 4
computing value for 3
first iteration: 3 => 9
computing value for 4
first iteration: 4 => 16
break after first iteration
second iteration: 0 => 0
second iteration: 1 => 1
second iteration: 2 => 4
second iteration: 3 => 9
second iteration: 4 => 16
computing value for 5
second iteration: 5 => 25
computing value for 6
second iteration: 6 => 36
computing value for 7
second iteration: 7 => 49
break after second iteration
partial iteration: 5 => 25
partial iteration: 6 => 36
partial iteration: 7 => 49
computing value for 8
partial iteration: 8 => 64
computing value for 9
partial iteration: 9 => 81
<?php
abstract class LazyIterator implements Iterator, Countable, ArrayAccess
{
private $values;
private $results = array();
private $index = 0;
public function __construct(array $values)
{
$this->values = array_values($values);
}
abstract protected function compute($index, $value);
/**
* @see Iterator::current()
*/
public function current()
{
if (false === array_key_exists($this->index, $this->results)) {
$this->results[$this->index] = $this->compute($this->index, $this->values[$this->index]);
}
return $this->results[$this->index];
}
/**
* @see Iterator::key()
*/
public function key()
{
return $this->index;
}
/**
* @see Iterator::next()
*/
public function next()
{
$this->index++;
}
/**
* @see Iterator::rewind()
*/
public function rewind()
{
$this->index = 0;
}
/**
* @see Iterator::value()
*/
public function valid()
{
return isset($this->values[$this->index]);
}
/**
* @see Countable::count()
*/
public function count()
{
return count($this->values);
}
/**
* @see ArrayAccess::offsetExists()
*/
public function offsetExists($index)
{
return in_array($index, $this->values);
}
/**
* @see ArrayAccess::offsetGet()
*/
public function offsetGet($index)
{
if (false === array_key_exists($index, $this->results)) {
if (false === array_key_exists($index, $this->values)) {
throw new RuntimeException("invalid index: $index");
}
$this->results[$index] = $this->compute($index, $this->values[$index]);
}
return $this->results[$index];
}
/**
* @see ArrayAccess::offsetSet()
*/
public function offsetSet($offset, $value)
{
throw new RuntimeException("LazyIterator is read-only");
}
/**
* @see ArrayAccess::offsetUnset()
*/
public function offsetUnset($offset)
{
throw new RuntimeException("LazyIterator is read-only");
}
}
<?php
require 'LazyIterator.php';
header('Content-type: text/plain');
/**
* A sample iterator that computes the square of it's values
*/
class SquareIterator extends LazyIterator
{
protected function compute($index, $value)
{
echo "computing value for {$value}\n";
return $value * $value;
}
}
// create some input:
$input = array_keys(array_fill(0, 10, null));
$squares = new SquareIterator($input);
// do a partial loops over the results:
foreach ($squares as $index => $value) {
echo "first iteration: $index => $value\n";
if ($index === 4) {
echo "break after first iteration\n";
break;
}
}
// do another partial loop a bit further this time, to show that
// (A) values are computed only once, and
// (B) additional values are computed as needed
foreach ($squares as $index => $value) {
echo "second iteration: $index => $value\n";
if ($index === 7) {
echo "break after second iteration\n";
break;
}
}
// do another loop, this time starting from a wild offset:
for ($index=5; $index<count($squares); $index++) {
echo "partial iteration: {$index} => {$squares[$index]}\n";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment