From 61525505390cd8fc81282c1567f2e3295aa90913 Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Sun, 1 May 2016 13:10:28 +0200 Subject: [PATCH 1/2] Initial implementation of processing A processing function can handle the processing of elements passed to it in the following ways: 1. Do something with and pass it on down the chain, just like peek() 2. Do something with and return a result, just like map() 3. Decide not to pass it on, just like filter() 4. Decide it cannot be handled now, and to retry it later (no equivalent) 5. Defer its passing down the chain to a later point (no equivalent) --- src/main/php/util/data/Processing.class.php | 94 +++++++++++++++++++ src/main/php/util/data/Sequence.class.php | 11 +++ .../data/unittest/ProcessingTest.class.php | 50 ++++++++++ 3 files changed, 155 insertions(+) create mode 100755 src/main/php/util/data/Processing.class.php create mode 100755 src/test/php/util/data/unittest/ProcessingTest.class.php diff --git a/src/main/php/util/data/Processing.class.php b/src/main/php/util/data/Processing.class.php new file mode 100755 index 0000000..a047981 --- /dev/null +++ b/src/main/php/util/data/Processing.class.php @@ -0,0 +1,94 @@ +it= $it; + $this->func= $func; + } + + private function process($key, $value) { + $this->key= $key; + $result= $this->func->__invoke($this, $value); + if (null === $this->key) { + return false; + } else { + $this->current= null === $result ? $value : $result; + return true; + } + } + + public function defer($value, $key= null) { + null === $key && $key= $this->key; + $this->defer[]= function() use($key, $value) { + $this->key= $key; + $this->current= $value; + return true; + }; + $this->key= null; + } + + public function drop($value, $key= null) { + $this->key= null; + } + + public function retry($value, $key= null) { + null === $key && $key= $this->key; + $this->defer[]= function() use($key, $value) { + return $this->process($key, $value); + }; + $this->key= null; + } + + /** @return void */ + private function forward() { + while ($this->it->valid()) { + if ($this->process($this->it->key(), $this->it->current())) { + $this->valid= true; + return; + } + + $this->it->next(); + } + + if ($this->valid= !empty($this->defer)) { + do { + $handled= array_shift($this->defer); + } while (!$handled()); + } + } + + /** @return void */ + public function rewind() { + $this->it->rewind(); + $this->forward(); + } + + /** @return void */ + public function next() { + $this->it->next(); + $this->forward(); + } + + /** @return var */ + public function current() { return $this->current; } + + /** @return var */ + public function key() { return $this->key; } + + /** @return bool */ + public function valid() { return $this->valid; } +} \ No newline at end of file diff --git a/src/main/php/util/data/Sequence.class.php b/src/main/php/util/data/Sequence.class.php index 866d81c..326190c 100755 --- a/src/main/php/util/data/Sequence.class.php +++ b/src/main/php/util/data/Sequence.class.php @@ -378,6 +378,17 @@ public function map($function) { return new self($m); } + /** + * Returns a new stream which calls a processing function for each element. + * + * @param function(var): var $function + * @return self + * @throws lang.IllegalArgumentException + */ + public function process($function) { + return new self(new Processing($this->getIterator(), Functions::$APPLY->newInstance($function))); + } + /** * Returns a new stream which flattens, mapping the given function to each * element. diff --git a/src/test/php/util/data/unittest/ProcessingTest.class.php b/src/test/php/util/data/unittest/ProcessingTest.class.php new file mode 100755 index 0000000..1486f15 --- /dev/null +++ b/src/test/php/util/data/unittest/ProcessingTest.class.php @@ -0,0 +1,50 @@ +process(function($processing, $i) { + if (0 === $i % 2) $processing->defer($i); + }) + ->map(function($i) { return 2 * $i; }) + ; + + $this->assertEquals([2, 6, 4, 8], $result->toArray()); + } + + #[@test] + public function drop() { + $result= Sequence::of([1, 2, 3, 4]) + ->process(function($processing, $i) { + if (0 === $i % 2) $processing->drop($i); + }) + ->map(function($i) { return 2 * $i; }) + ; + + $this->assertEquals([2, 6], $result->toArray()); + } + + #[@test] + public function retry() { + $result= Sequence::of(['hello', ' hi']) + ->process(function($processing, $index) { + static $map= ['hello' => 1, 'hi' => 2]; + + try { + return $map[$index]; + } catch (IndexOutOfBoundsException $t) { + $processing->retry(substr($index, 1)); + } + }) + ->map(function($i) { return 2 * $i; }) + ; + + $this->assertEquals([2, 4], $result->toArray()); + } +} \ No newline at end of file From 4d73cc7e56646849502444b850fe849c7ab167e2 Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Sun, 1 May 2016 13:21:45 +0200 Subject: [PATCH 2/2] Add ability to execute a closure before retrying --- src/main/php/util/data/Processing.class.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/php/util/data/Processing.class.php b/src/main/php/util/data/Processing.class.php index a047981..e707a3f 100755 --- a/src/main/php/util/data/Processing.class.php +++ b/src/main/php/util/data/Processing.class.php @@ -31,8 +31,8 @@ private function process($key, $value) { } } - public function defer($value, $key= null) { - null === $key && $key= $this->key; + public function defer($value) { + $key= $this->key; $this->defer[]= function() use($key, $value) { $this->key= $key; $this->current= $value; @@ -41,13 +41,14 @@ public function defer($value, $key= null) { $this->key= null; } - public function drop($value, $key= null) { + public function drop($value) { $this->key= null; } - public function retry($value, $key= null) { - null === $key && $key= $this->key; - $this->defer[]= function() use($key, $value) { + public function retry($value, $closure= null) { + $key= $this->key; + $this->defer[]= function() use($key, $value, $closure) { + $closure && $closure(); return $this->process($key, $value); }; $this->key= null;