jeremeamia / iter8
Requires
- php: >=7.2
Requires (Dev)
- phpstan/phpstan: ^0.11.0
- phpunit/phpunit: ^8.0
- squizlabs/php_codesniffer: ^3.4
This package is auto-updated.
Last update: 2024-09-20 10:20:02 UTC
README
简介
Iter8 是一个使用生成器实现的 PHP 库,用于迭代和函数式操作(例如 map、filter、reduce)。
Iter8 提供了创建和转换任何 iterable
(例如生成器、迭代器、数组等)的方法,以便轻松处理符合 Iterator
模式用例的数据集(即,通常是大型、分页、无限或长度未知的数据)。使用迭代器/生成器通常可以提供降低内存消耗和惰性评估等好处。可以通过函数组合来定义复杂的转换。
用法
Iter8 的核心实现位于 3 个类中作为静态方法
Iter
– 对可迭代值的操作。一些转换(例如 map、filter)和一些评估(例如 reduce)。Gen
– 从其他值创建可迭代对象的工厂。Func
– 用于创建或转换可调用对象以用于可迭代操作的实用工具。
Iter8 中有 3 种处理可迭代值的用法模式。您使用哪种模式主要取决于个人偏好
Iter
函数 – 使用Iter
函数的标准面向函数的使用。Pipe
组合 – 使用Iter::pipe()
和Pipe::*
函数组合一系列可迭代转换。Collection
对象 - Iter8 的面向对象的接口,暴露了Iter::*
和Gen::*
函数作为集合类型对象上的可链接方法。
下一节中的示例将演示这些用法模式中的每一个。
示例
给定以下数据集...
const PEOPLE = [ ['name' => 'Abby', 'age' => 19], ['name' => 'Benny', 'age' => 21], ['name' => 'Cally', 'age' => 22], ['name' => 'Danny', 'age' => 24], ['name' => 'Danny', 'age' => 24], ['name' => 'Eddy', 'age' => 18], ];
Iter 函数
在此用法模式中,操作按程序性和逐个应用。
$iter = Gen::from(PEOPLE); $iter = Iter::filter($iter, Func::compose([ Func::index('age'), Func::operator('>=', 20), ])); $iter = Iter::map($iter, Func::index('name')); $iter = Iter::debounce($iter); Iter::print($iter); #> ['Benny', 'Cally', 'Danny']
Pipe 组合
在此用法模式中,操作“管道”或组合在一起。`Pipe` 类将其操作委派给 `Iter` 类,但管理可迭代值。
$iter = Iter::pipe(Gen::from(PEOPLE), [ Pipe::filter(Func::compose([ Func::index('age'), Func::operator('>=', 20), ])), Pipe::map(Func::index('name')), Pipe::debounce(), ]); Iter::print($iter); #> ['Benny', 'Cally', 'Danny']
您可以在管道中间“切换”正在转换的可迭代值的上下文。此示例评估人员的最大年龄,然后切换到使用该最大年龄值的新可迭代值。
$iter = Iter::pipe(Gen::from(PEOPLE), [ Pipe::pluck('age'), Pipe::reduce('max'), Pipe::switch(function (int $maxAge) { return Gen::range(1, $maxAge); }), ]); Iter::print($iter); #> [1, 2, 3, ..., 22, 23, 24]
Collection 对象
在此用法模式中,可迭代值封装为 Collection
对象。在集合对象上调用方法将委派回 `Iter` 类,但可迭代值在内部管理。集合是不可变的,因此每个转换都返回一个新的实例。此外,与常规生成器不同,集合可以被重置。`Collection` 上的静态方法调用委派给 `Gen`,因此 `Collection` 对象实际上从一个接口暴露了 Iter8 的全部功能。
$collection = Collection::from(PEOPLE) ->filter(Func::compose([ Func::index('age'), Func::operator('>=', 20), ])) ->map(Func::index('name')) ->debounce(); $collection->print(); #> ['Benny', 'Cally', 'Danny']
可重置性
生成器是不可重置的(即,显式调用 rewind()
或再次迭代它们将导致错误)。Iter8 提供了两种使生成器/可迭代对象可重置的方法。
延迟生成器
如果您控制生成生成器的函数(即包含 yield
语句的函数),则可以使用 Gen::defer()
函数包装该生成函数。
$items = Gen::defer(function () use ($apiClient) { $apiResult = $apiClient->getItems(); foreach ($apiResult['items'] as $data) { yield Models\Item::fromArray($data); } }); // ... // First iteration foreach ($items as $item) { /* ... */ } // ... // Another iteration foreach ($items as $item) { /* ... */ }
Gen::defer()
返回一个 DeferredGenerator
迭代器,它延迟在迭代时产生实际的生成器。如果您重置或再次迭代,则生成函数将重新执行。
可重置迭代器
如果您不控制生成函数或想避免重新执行生成器,那么您可以使用 Iter::rewindable()
函数使可迭代对象具有可回滚功能。
$apiResult = $apiClient->getItems(); $items = Iter::map($apiResult['items'], function (array $data) { return Models\Item::fromArray($data); }); $items = Iter::rewindable($items); // ... // First iteration foreach ($items as $item) { /* ... */ } // ... // Another iteration foreach ($items as $item) { /* ... */ }
Iter::rewindable()
将提供的可迭代对象包装在一个 RewindableIterator
中,它在第一次迭代期间缓存项,以便在后续迭代中重新发射。
此外,由于它们扩展了 RewindableIterator
,Collection
默认是可回滚的。
灵感来源
我最近在 PHP 中的许多工作都涉及到迭代器和生成器,以及处理大量的 API 结果集,所以我想要整理一些内容来分享。
然而,在诸如 nikic/iter 这样的库中已经完成了一些类似的工作。您会注意到我有一些类似的函数和实现。我的一些工作受到了该库的启发,还有一些是直接借鉴的。
此外,我还从 ReactiveX 项目中获得了灵感。尽管生成器和可观察者不是完全相同的概念,但它们是相似的,因此当这些操作和操作名称对生成器和可观察者都同样适用时,我会借鉴它们。此外,来自 RxJS(ReactiveX 的 JavaScript 实现)的“管道”概念也被用于此项目中以组合转换。从某种意义上说,这也类似于 mtdowling/transducers.php 库如何使用其“可组合算法转换”。而“转换器”的概念本身是从 Clojure 借鉴的,所以我从多个来源寻找灵感。
最后,我还从 Laravel Collections 库中获得了灵感。尽管我也有一些类似的函数,但我的实现有很大的差异,因为它们基于生成器,而不是数组。这意味着不支持在 Iter8 集合中对值的随机数组访问。