Skip to content

Commit f7d7a3c

Browse files
committed
Class DynamicSequence added.
1 parent 43fd437 commit f7d7a3c

4 files changed

Lines changed: 283 additions & 3 deletions

File tree

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,22 @@ foreach($sequence as $value) {
155155
// 0.5 0.25 0.125...
156156
```
157157

158+
#### DynamicSequence
159+
160+
```php
161+
use Smoren\Sequence\Structs\DynamicSequence;
162+
163+
// (from, size, nextValueGetter, indexValueGetter)
164+
$sequence = new DynamicSequence(1, 5, static function($previousValue) {
165+
return $previousValue + 1;
166+
}, static function($index, $startValue) {
167+
return $startValue + $index;
168+
});
169+
170+
var_dump(iterator_to_array($sequence));
171+
// [1, 2, 3, 4, 5]
172+
```
173+
158174
#### IndexedArray
159175

160176
```php

src/Structs/DynamicSequence.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
namespace Smoren\Sequence\Structs;
4+
5+
use function Smoren\Sequence\Functions\reduce;
6+
use function Smoren\Sequence\Functions\xrange;
7+
8+
/**
9+
* Implementation of sequence configured with callables.
10+
*
11+
* @template T
12+
* @extends Sequence<T>
13+
*/
14+
class DynamicSequence extends Sequence
15+
{
16+
/**
17+
* @var callable(T $previousValue): T getter for the next value
18+
*/
19+
protected $nextValueGetter;
20+
/**
21+
* @var (callable(int $index, T $start): T)|null getter for the i-th value
22+
*/
23+
protected $indexValueGetter;
24+
25+
/**
26+
* StepSequence constructor.
27+
*
28+
* @param T $start start of the sequence
29+
* @param int<0, max>|null $size size of the sequence (infinite if null)
30+
* @param callable(T $previousValue): T $nextValueGetter getter for the next value
31+
* @param (callable(int $index, T $start): T)|null $indexValueGetter getter for the i-th value
32+
*/
33+
public function __construct(
34+
$start,
35+
?int $size,
36+
callable $nextValueGetter,
37+
?callable $indexValueGetter = null
38+
) {
39+
parent::__construct($start, $size);
40+
$this->nextValueGetter = $nextValueGetter;
41+
$this->indexValueGetter = $indexValueGetter;
42+
}
43+
44+
/**
45+
* {@inheritDoc}
46+
*/
47+
public function getNextValue($previousValue)
48+
{
49+
return ($this->nextValueGetter)($previousValue);
50+
}
51+
52+
/**
53+
* {@inheritDoc}
54+
*/
55+
public function getValueByIndex(int $index)
56+
{
57+
if(is_callable($this->indexValueGetter)) {
58+
return ($this->indexValueGetter)($index, $this->start);
59+
}
60+
61+
return reduce(xrange($index), function($carry) {
62+
return $this->getNextValue($carry);
63+
}, $this->start);
64+
}
65+
}

src/functions.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,10 @@ function filter(iterable $collection, callable $filter): IndexedArray
7575
* @template TOutput
7676
*
7777
* @param iterable<TInput> $collection
78-
* @param callable(TOutput|null $carry, TInput $item): TOutput $reducer
79-
* @param TOutput|null $initialValue
78+
* @param callable(TOutput $carry, TInput $item): TOutput $reducer
79+
* @param TOutput $initialValue
8080
*
81-
* @return TOutput|null
81+
* @return TOutput
8282
*/
8383
function reduce(iterable $collection, callable $reducer, $initialValue = null)
8484
{

tests/unit/DynamicSequenceTest.php

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Smoren\Sequence\Tests\Unit;
6+
7+
use Codeception\Test\Unit;
8+
use Smoren\Sequence\Exceptions\ReadOnlyException;
9+
use Smoren\Sequence\Structs\DynamicSequence;
10+
11+
class DynamicSequenceTest extends Unit
12+
{
13+
protected const PRECISION = 0.00001;
14+
15+
/**
16+
* @dataProvider dataProviderForEqualNonInfinite
17+
* @param array $config
18+
* @param callable $nextValueGetter
19+
* @param callable|null $indexValueGetter
20+
* @param array $expected
21+
* @return void
22+
*/
23+
public function testEqualNonInfinite(
24+
array $config,
25+
callable $nextValueGetter,
26+
?callable $indexValueGetter,
27+
array $expected
28+
): void {
29+
// Given
30+
$sequence = new DynamicSequence(...[...$config, $nextValueGetter, $indexValueGetter]);
31+
32+
// When
33+
$result = iterator_to_array($sequence);
34+
35+
// Then
36+
$this->assertFalse($sequence->isInfinite());
37+
$this->assertEqualsWithDelta($expected, $result, self::PRECISION);
38+
$this->assertEquals(count($sequence), count($expected));
39+
40+
// iterating and accessing by indexes checks
41+
$iterationsCount = 0;
42+
foreach($sequence as $index => $value) {
43+
$this->assertEqualsWithDelta($expected[$index], $value, self::PRECISION);
44+
$this->assertEqualsWithDelta($expected[$index], $sequence[$index], self::PRECISION);
45+
46+
$negativeIndex = -$index - 1;
47+
$reverseIndex = count($expected) + $negativeIndex;
48+
49+
$this->assertEqualsWithDelta($expected[$reverseIndex], $sequence[$negativeIndex], self::PRECISION);
50+
++$iterationsCount;
51+
}
52+
53+
$this->assertEquals(count($expected), $iterationsCount);
54+
55+
// readonly checks
56+
foreach($sequence as $index => $value) {
57+
try {
58+
$sequence[$index] = -1000;
59+
$this->fail();
60+
} catch(ReadOnlyException $e) {
61+
$this->assertEqualsWithDelta($value, $sequence[$index], self::PRECISION);
62+
}
63+
}
64+
}
65+
66+
/**
67+
* @return array
68+
*/
69+
public function dataProviderForEqualNonInfinite(): array
70+
{
71+
return [
72+
[
73+
[0, 5],
74+
static function($previousValue) {
75+
return $previousValue;
76+
},
77+
static function() {
78+
return 0;
79+
},
80+
[0, 0, 0, 0, 0]
81+
],
82+
[
83+
[0, 5],
84+
static function($previousValue) {
85+
return $previousValue;
86+
},
87+
null,
88+
[0, 0, 0, 0, 0]
89+
],
90+
[
91+
[0, 5],
92+
static function($previousValue) {
93+
return $previousValue + 1;
94+
},
95+
static function($index, $start) {
96+
return $start + $index;
97+
},
98+
[0, 1, 2, 3, 4]
99+
],
100+
[
101+
[0, 5],
102+
static function($previousValue) {
103+
return $previousValue + 1;
104+
},
105+
null,
106+
[0, 1, 2, 3, 4]
107+
],
108+
[
109+
[1, 5],
110+
static function($previousValue) {
111+
return $previousValue + 1;
112+
},
113+
static function($index, $start) {
114+
return $start + $index;
115+
},
116+
[1, 2, 3, 4, 5]
117+
],
118+
[
119+
[1, 5],
120+
static function($previousValue) {
121+
return $previousValue + 1;
122+
},
123+
null,
124+
[1, 2, 3, 4, 5]
125+
],
126+
];
127+
}
128+
129+
/**
130+
* @dataProvider dataProviderForInfinite
131+
* @param array $config
132+
* @param callable $nextValueGetter
133+
* @param callable|null $indexValueGetter
134+
* @param array $expectedDirect
135+
* @param array $expectedReverse
136+
* @return void
137+
*/
138+
public function testInfinite(
139+
array $config,
140+
callable $nextValueGetter,
141+
?callable $indexValueGetter,
142+
array $expectedDirect,
143+
array $expectedReverse
144+
): void
145+
{
146+
// Given
147+
$resultDirect = [];
148+
$resultReverse = [];
149+
$range = new DynamicSequence(...[...$config, $nextValueGetter, $indexValueGetter]);
150+
151+
// When
152+
foreach($range as $value) {
153+
$resultDirect[] = $value;
154+
155+
if(count($resultDirect) === count($expectedDirect)) {
156+
break;
157+
}
158+
}
159+
for($i=0; $i<count($expectedReverse); ++$i) {
160+
$resultReverse[] = $range[-$i-1];
161+
}
162+
163+
// Then
164+
$this->assertTrue($range->isInfinite());
165+
$this->assertEqualsWithDelta($expectedDirect, $resultDirect, self::PRECISION);
166+
$this->assertEqualsWithDelta($expectedReverse, $resultReverse, self::PRECISION);
167+
}
168+
169+
/**
170+
* @return array
171+
*/
172+
public function dataProviderForInfinite(): array
173+
{
174+
return [
175+
[
176+
[22, null],
177+
static function($previousValue) {
178+
return $previousValue;
179+
},
180+
static function($index, $startValue) {
181+
return $startValue;
182+
},
183+
array_fill(0, 100, 22),
184+
array_fill(0, 100, 22),
185+
],
186+
[
187+
[0, null],
188+
static function($previousValue) {
189+
return $previousValue + 1;
190+
},
191+
static function($index, $startValue) {
192+
return $startValue + $index;
193+
},
194+
range(0, 99, 1),
195+
range(-1, -100, 1.0),
196+
],
197+
];
198+
}
199+
}

0 commit comments

Comments
 (0)