harmenjanssen / lazycollection
使用生成器的功能迭代模式。
Requires
- php: >=7.0.0
Requires (Dev)
- phpunit/phpunit: ^5.7
This package is auto-updated.
Last update: 2024-09-17 18:41:46 UTC
README
这是什么?
LazyCollection
类,负责为您的集合创建生成器。
生成器在 PHP 中不与标准 map
、filter
和 reduce
函数一起工作。
这当然是一种遗憾,因为您会失去许多便利的抽象。本软件包懒惰地实现了这些方法。
考虑以下传统数组的示例
$square = function($x) { return $x * $x; }; $isEven = function($x) { return $x % 2 === 0; }; $highestEvenSquare = array_filter( array_map( $square, range(30, 0) ), $isEven )[0];
这需要在创建整个范围并将其映射到平方数和过滤之前,才能获取第一个项目。
使用 LazyCollection
,可以像这样实现
$lazyCollection = new LazyCollection\Examples\LazyArray(range(29, 0)); $lazyCollection->map($square) ->filter($isEven) ->first();
当然,初始范围将在内存中创建,但只需要对前两个项目进行平方和过滤。由于第二个项目 28 符合我们的过滤器函数(28 * 28 结果是偶数 784),因此 first()
只会停止在这里。其余的平方数将不会计算。
可以想象一个集合,其中每个后续项目都会立即计算,在这种情况下,原始数组甚至不必在内存中创建(例如:搜索大型数据库或文件,直到找到固定数量的匹配项)。
安装
使用 Composer 安装
composer require harmenjanssen/lazycollection
用法
all()
- 此方法返回实际的
Generator
$numbers = LazyCollection\Examples\Numbers::from(1); foreach ($numbers->take(100)->all() as $n) { echo $n . "\n"; } // will echo numbers 1 through 100
first()
- 获取集合的第一个项目。
take(int $amount): Subset
- 将集合限制为给定数量。返回一个新的
LazyCollection
,类型为Subset
。请参阅all()
下的示例。
slice(int $start, int $length): Subset
- 从
$start
开始获取集合的$length
个项目。
$numbers = LazyCollection\Examples\Numbers::from(1); $fifty = $numbers->slice(50, 10); iterator_to_array($fifty); // [50, 51, ... 60]
map(callable $transformer): Mapped
- 向集合添加映射函数。返回一个新的
LazyCollection
,类型为Mapped
。
$numbers = LazyCollection\Examples\Numbers::from(1); $squares = $numbers->map(function($x) { return $x * $x; }); iterator_to_array($squares->take(10)); // [1, 4, 9, 16, 25... 100]
filter(callable $predicate): Filtered
- 向集合添加过滤器函数。返回一个新的
LazyCollection
,类型为Filtered
。
$numbers = LazyCollection\Examples\Numbers::from(1); $evenStevens = $numbers->filter(function($x) { return $x % 2 === 0; }); iterator_to_array($evenStevens->take(10)); // [2, 4, 6, 8, 10... 20]
reduce(callable $reducer, $seed)
- 将集合缩减为单个值。
$numbers = LazyCollection\Examples\Numbers::from(1)->take(10); $sum = $numbers->reduce(function($a, $b) { return $a + $b; }, 0); // 55
实现自己的懒惰集合
从 LazyCollection
创建子类,实现 4 个必需的方法
start()
- 返回初始迭代。注意,这不同于迭代的值,也不一定是数字。您确定如何迭代您的集合以及您需要什么数据来通过它。
next($iteration)
- 基于前一个迭代返回下一个迭代。注意,返回类型及其参数的类型与
start()
相同,可能性很大。
done(): bool
- 迭代是否完成。
value($iteration)
- 根据给定的迭代返回一个值。
无限集合
无限集合是计算模式的优雅解决方案。无论是 Numbers 序列还是 Fibonacci 序列,都是无限集合的示例。
您可以为值范围投影,但没有自然结束。
尽管遍历这些集合没有问题,但您必须确保自己停止迭代,否则内存会耗尽。take()
是这种情况下的一个便捷方法。
$fibonacci = new LazyCollection\Examples\Fibonacci; iterator_to_array($fibonacci->take(10)->all()); // [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
还要注意这个陷阱:当通过一个没有任何匹配项的谓词过滤无限集合时,过滤器不会返回一个空集合,就像它对常规数组所做的那样。由于它永远不会结束,所以它会耗尽内存。
// This will never resolve: $fibonacci = new LazyCollection\Examples\Fibonacci; $fibonacci->filter('is_string')->take(10);
此外,您不能使用iterator_to_array
将迭代器扁平化为数组。同样,这也永远不会结束。
待办事项
- 实现
until()
- 实现
find()
灵感来源
我从Reginald Braithwaite的精彩书籍《Javascript Allongé》中获得了功能迭代器的概念,该书可在Reginald Braithwaite的GitHub上找到。
许多有趣的语言支持惰性(可能是无限的)序列的概念,如Haskell和Clojure。