camille-hdl / lazy-lists
惰性列表处理和转换器
Requires
- php: ^8.1
Requires (Dev)
- mockery/mockery: ^1.6
- php-parallel-lint/php-parallel-lint: ^1.3
- phpbench/phpbench: ^1.2
- phpstan/phpstan: ^1.10
- phpstan/phpstan-mockery: ^1.1
- phpunit/phpunit: ^10.2
- squizlabs/php_codesniffer: ^3.7
This package is auto-updated.
Last update: 2024-09-19 15:24:32 UTC
README
LazyLists 是一个用于遍历和转换 Traversable
的工具箱。这是你的典型 map
、filter
、pipe
库,但是有一个特点:你最多只迭代一次。
使用 \LazyLists\pipe()
,你可以将 filter
、map
、reduce
、each
、until
(...) 组合成一个单次遍历输入的单个函数(如果你使用 take
或 until
,甚至可能少于一次),因此称为“惰性”。
例如,假设我们想要计算一个在线商店技术分类中前50个订单产品的某些信息
$getUsefulInsight = pipe( map($getProductsInOrder), flatten(1), filter($isTechnologyRelated), take(50), reduce($computeInsight, $initialValue) ); $insight = $getUsefulInsight($orders);
即使 $orders
非常大,$getUsefulInsight
也只会遍历到 $isTechnologyRelated
允许50个项目通过(或者 $orders
在那之前就用完了),然后提前停止迭代并返回 $computeInsight
的最终结果。
$getProductsInOrder
和 $isTechnologyRelated
只在需要时调用。
如果迭代成本很高,这尤其有用。
或者,您可以直接使用这些函数:$output = \LazyLists\map($transformation, $input)
。
您可以在数组或类似迭代器的 Traversable
上使用这些功能。
请参阅下面的示例。
安装
首选的安装方法是使用 Composer。运行以下命令安装包并将其添加到项目的 composer.json
中的要求
composer require camille-hdl/lazy-lists
使用方法
您可以直接在数组或 Traversable
上使用这些函数
use function LazyLists\map; use function LazyLists\filter; use function LazyLists\reduce; use function LazyLists\flatten; use function LazyLists\take; $result = map($fn, $input); $result = filter($fn, $input); $result = reduce($fn, [], $input); $result = flatten(1, $input); $result = take(10, $input);
但使用 LazyLists 最有趣的方法是使用组合函数:pipe
或 iterate
。
管道中的步骤将按顺序执行 每个 集合中的 单个迭代,与 array_map
、array_filter
或其他类似库不同。
use function LazyLists\pipe; use function LazyLists\iterate; use function LazyLists\map; use function LazyLists\filter; use function LazyLists\reduce; use function LazyLists\each; use function LazyLists\flatten; use function LazyLists\take; use function LazyLists\until; /** * Compose steps together into a single function */ $computeFinalResult = pipe( flatten(1), filter($myPredicate), map($myTransformation), each($mySideEffect), take(10), reduce($myAggregator, 0) ); // returns an array $result = $computeFinalResult($myArrayOrIterator); // returns an iterator $filterIterator = iterate( filter($myPredicate), until($myCondition) ); foreach ($filterIterator($myArrayOrIterator) as $key => $value) { echo "$key : $value"; } // you can iterate over a reduction $reduceIterator = iterate( reduce(function ($acc, $v) { return $acc + $v; }, 0), until(function ($sum) { return $sum > 10; }) ); foreach ($reduceIterator([1, 5, 10]) as $reduction) { echo $reduction; } // 1, 5
注意事项
- 设计上,遍历数组时永远不会保留键。
pipe(...)($input)
的返回值取决于组合中的最后一个函数,以下启发式方法对我来说很直观,但可能对您来说不一定- 如果最后一个函数返回单个值,例如
reduce()
,则返回值将是此值; - 如果最后一个函数将输入集合转换为输出集合(例如
map
或flatten
),则返回值将是输出集合(作为一个扁平数组); - 如果最后一个函数限制输出集合,例如
filter
、take
或until
,则返回值将是输出集合(作为一个扁平数组);
- 如果最后一个函数返回单个值,例如
iterate(...)($input)
的行为相同,但返回一个迭代器而不是数组。
扩展 LazyLists
- 您可以通过使其返回一个
\Lazy\Transducer\TransducerInterface
来创建自己的函数,该函数可用于pipe()
和iterate()
。 - 您可以创建自己的组合函数(代替
pipe()
或iterate()
)。例如,实现可以接受流作为输入的功能应该相对容易。查看pipe()
(它非常短!)和\Lazy\LazyWorker
(它不太短!)的源代码。
性能考虑
LazyLists 优化了最小化迭代次数,同时(希望)在转换输入集时允许 API 优雅。
因此,迭代的成本越低,使用这个库的激励就越少。
如果你的程序只是迭代快速、内存中的数据结构,性能几乎总是比使用内置的 array_*
函数要差。但是,如果你使用 \LazyLists\pipe()
来组合你的函数,随着迭代次数的增加,性能差距会减小。
你可以通过运行 composer run phpbench
来亲自验证。
然而,如果你在使用 \LazyLists\pipe()
时,以下情况可能会带来性能上的好处:
- 在迭代过程中使用 I/O,或者
- 使用
\LazyLists\take()
和\LazyLists\until()
来限制你的输出集。
灵感
这个库尝试实现转换器(transducers)功能的一个子集。已经有转换器库存在,但我希望提供一个更简单的 API。
贡献
本项目遵循贡献者行为准则。通过参与本项目及其社区,你应遵守此准则。
欢迎贡献!请参阅贡献指南以获取详细信息。
版权和许可证
camille-hdl/lazy-lists 库的版权归 Camille Hodoul 所有,并许可在 MIT 许可证(MIT)下使用。请参阅LICENSE 以获取更多信息。