camille-hdl/lazy-lists

惰性列表处理和转换器

v0.3.1 2023-07-19 12:43 UTC

This package is auto-updated.

Last update: 2024-09-19 15:24:32 UTC


README

Source Code Latest Version Software License PHP Version Build Status Coverage Status Total Downloads

LazyLists 是一个用于遍历和转换 Traversable 的工具箱。这是你的典型 mapfilterpipe 库,但是有一个特点:你最多只迭代一次。

使用 \LazyLists\pipe(),你可以将 filtermapreduceeachuntil(...) 组合成一个单次遍历输入的单个函数(如果你使用 takeuntil,甚至可能少于一次),因此称为“惰性”。

例如,假设我们想要计算一个在线商店技术分类中前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 最有趣的方法是使用组合函数:pipeiterate
管道中的步骤将按顺序执行 每个 集合中的 单个迭代,与 array_maparray_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(),则返回值将是此值;
    • 如果最后一个函数将输入集合转换为输出集合(例如 mapflatten),则返回值将是输出集合(作为一个扁平数组);
    • 如果最后一个函数限制输出集合,例如 filtertakeuntil,则返回值将是输出集合(作为一个扁平数组);
  • 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 以获取更多信息。