harmenjanssen/lazycollection

使用生成器的功能迭代模式。

dev-master 2017-09-25 04:51 UTC

This package is auto-updated.

Last update: 2024-09-17 18:41:46 UTC


README

这是什么?

LazyCollection 类,负责为您的集合创建生成器。
生成器在 PHP 中不与标准 mapfilterreduce 函数一起工作。
这当然是一种遗憾,因为您会失去许多便利的抽象。本软件包懒惰地实现了这些方法。

考虑以下传统数组的示例

$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。