Skip to content

Commit 45da20a

Browse files
committed
Make lists event lazier
1 parent 726efe6 commit 45da20a

File tree

4 files changed

+74
-26
lines changed

4 files changed

+74
-26
lines changed

src/Functional/functions.php

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -295,15 +295,17 @@ function foldr(callable $callable, $accumulator = null, Foldable $foldable = nul
295295
function filter(callable $predicate, Listt $xs = null)
296296
{
297297
return curryN(2, function (callable $predicate, Listt $xs): Listt {
298-
try {
299-
return $predicate(head($xs))
300-
? new ListtCons(function () use ($predicate, $xs) : array {
301-
return [head($xs), filter($predicate, tail($xs))];
302-
})
303-
: filter($predicate, tail($xs));
304-
} catch (EmptyListError $e) {
305-
return fromNil();
306-
}
298+
return new ListtCons(function () use ($predicate, $xs) {
299+
try {
300+
return $predicate(head($xs))
301+
? new ListtCons(function () use ($predicate, $xs) {
302+
return [head($xs), filter($predicate, tail($xs))];
303+
})
304+
: filter($predicate, tail($xs));
305+
} catch (EmptyListError $e) {
306+
return fromNil();
307+
}
308+
});
307309
})(...func_get_args());
308310
}
309311

src/Functional/miscellaneous.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,21 @@
44

55
namespace Widmogrod\Functional;
66

7+
/**
8+
* @var callable
9+
*/
10+
const noop = 'Widmogrod\Functional\noop';
11+
12+
/**
13+
* noop :: _ -> _
14+
*
15+
* @return null
16+
*/
17+
function noop()
18+
{
19+
return null;
20+
}
21+
722
/**
823
* @var callable
924
*/

src/Primitive/ListtCons.php

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
use Widmogrod\Common;
88
use Widmogrod\FantasyLand;
99
use Widmogrod\Functional as f;
10+
use function Widmogrod\Functional\constt;
11+
use const Widmogrod\Functional\noop;
1012

1113
class ListtCons implements Listt, \IteratorAggregate
1214
{
@@ -39,7 +41,16 @@ public function getIterator()
3941
{
4042
$tail = $this;
4143
do {
42-
[$head, $tail] = $tail->headTail();
44+
$result = $tail->lazyHeadTail(function ($x, Listt $xs): array {
45+
return [$x, $xs];
46+
}, noop);
47+
48+
if (!is_array($result)) {
49+
return;
50+
}
51+
52+
[$head, $tail] = $result;
53+
4354
yield $head;
4455
} while ($tail instanceof self);
4556
}
@@ -50,9 +61,9 @@ public function getIterator()
5061
public function map(callable $transformation): FantasyLand\Functor
5162
{
5263
return new self(function () use ($transformation) {
53-
[$head, $tail] = $this->headTail();
54-
55-
return [$transformation($head), $tail->map($transformation)];
64+
return $this->lazyHeadTail(function ($x, Listt $xs) use ($transformation) {
65+
return [$transformation($x), $xs->map($transformation)];
66+
}, constt(self::mempty()));
5667
});
5768
}
5869

@@ -148,9 +159,9 @@ public function concat(FantasyLand\Semigroup $value): FantasyLand\Semigroup
148159

149160
if ($value instanceof self) {
150161
return new self(function () use ($value) {
151-
[$x, $xs] = $this->headTail();
152-
153-
return [$x, $xs->concat($value)];
162+
return $this->lazyHeadTail(function ($x, Listt $xs) use ($value) {
163+
return [$x, $xs->concat($value)];
164+
}, constt(self::mempty()));
154165
});
155166
}
156167

@@ -162,7 +173,7 @@ public function concat(FantasyLand\Semigroup $value): FantasyLand\Semigroup
162173
*/
163174
public function equals($other): bool
164175
{
165-
return $other instanceof self
176+
return $other instanceof Listt
166177
? $this->extract() === $other->extract()
167178
: false;
168179
}
@@ -172,23 +183,37 @@ public function equals($other): bool
172183
*/
173184
public function head()
174185
{
175-
[$head] = $this->headTail();
176-
177-
return $head;
186+
return $this->lazyHeadTail(function ($head) {
187+
return $head;
188+
}, function () {
189+
throw new EmptyListError(__FUNCTION__);
190+
});
178191
}
179192

180193
/**
181194
* @inheritdoc
182195
*/
183196
public function tail(): Listt
184197
{
185-
[$head, $tail] = $this->headTail();
186-
187-
return $tail;
198+
return $this->lazyHeadTail(function ($head, $tail) {
199+
return $tail;
200+
}, function () {
201+
throw new EmptyListError(__FUNCTION__);
202+
});
188203
}
189204

190-
public function headTail(): array
205+
private function lazyHeadTail(callable $headTail, callable $nil)
191206
{
192-
return call_user_func($this->next);
207+
$result = $this;
208+
209+
do {
210+
$result = call_user_func($result->next);
211+
} while ($result instanceof self);
212+
213+
if ($result instanceof ListtNil) {
214+
return $nil();
215+
}
216+
217+
return $headTail(...$result);
193218
}
194219
}

test/Functional/FilterTest.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44

55
namespace test\Functional;
66

7+
use Widmogrod\Primitive\Listt;
78
use function Widmogrod\Functional\filter;
89
use function Widmogrod\Functional\fromIterable;
910
use function Widmogrod\Functional\fromNil;
10-
use Widmogrod\Primitive\Listt;
11+
use function Widmogrod\Functional\repeat;
12+
use function Widmogrod\Functional\take;
1113

1214
class FilterTest extends \PHPUnit\Framework\TestCase
1315
{
@@ -47,6 +49,10 @@ public function provideData()
4749
'$list' => fromIterable(new \ArrayIterator([1, 2, 3, 4, 5])),
4850
'$expected' => fromIterable([1, 3, 5]),
4951
],
52+
'filter everything' => [
53+
'$list' => take(300, repeat(2)),
54+
'$expected' => fromNil(),
55+
],
5056
];
5157
}
5258
}

0 commit comments

Comments
 (0)