jumpifbelow/php-functional-array

用于使集合使用功能化的类

14.0.0 2022-06-11 15:48 UTC

README

开发阶段

该项目处于相当高级的开发阶段。从现在起,API 应该是稳定的,但仍然有可能进行重大更改。任何破坏性更改都将在主要版本中进行,无论是什么。

一般说明

此软件包包含3个接口和3个实现它们的类。每个接口的目的

  • FluentArrayInterface:允许以面向对象的方式使用原生 PHP 函数。
  • ExtendedArrayInterface:类似于 FluentArrayInterface,但实现了自定义操作。如果您寻求更多功能,则应使用此接口。如果您更愿意确保 PHP 的原生性能,避免使用此类将确保调用方法使用原生 PHP 函数。
  • FluentIteratorInterface:受 FluentArrayInterface 启发,但适用于几乎无限的集合。它具有较少的函数,因为许多函数提供没有意义,但它可以处理大型集合而不会耗尽内存。它将仅在迭代时执行更改方法,允许您精确控制代码将执行的位置,而不是在调用时。它将更好地处理任何提供数据的流或资源。

如何安装它?

只需运行 Composer

composer require jumpifbelow/php-functional-array

如何使用它?

创建了两种类型的数组。

第一种,JumpIfBelow\Arrays\FluentArrayInterface,旨在像 PHP 数组一样工作,所有内置函数都直接在方法中。所有调用都是不可变的和流畅的,这意味着您可以链式调用而不会更改第一个引用。唯一的例外是在管理内部指针时,它不会发送新对象,因为这会破坏迭代器。

第二种,JumpIfBelow\Arrays\ExtendedArrayInterface,旨在类似于 JumpIfBelow\Arrays\FluentArrayInterface。然而,由于它缺少一些常见的方法,它添加了这些方法以便更容易处理。

当然,软件包包含可以像这样使用的实现

<?php

use JumpIfBelow\Arrays\FluentArray;

$a = FluentArray::from([
    5,
    'er',
    'ert',
    'loop',
    false,
    null,
]);

$newArray = $a
    ->filter(function ($x): bool {
        return is_string($x);
    })
    ->map(function (string $x): string {
        return strrev($x);
    })
    ->sort()
;

var_dump($newArray->toArray());

// those examples could be shortened because they are using defined functions
// note that the interface accepts any callable, which allow this writing

$newArray = $a
    ->filter('is_string')
    ->map('strrev')
    ->sort()
;

// you can quickly setup an array and directly operate with it too
$pairSquareSum = FluentArray
    ::from([1, 4, 6, 5, 9, 8, 0])
    ->filter(fn(int $x) => $x % 2 === 0)
    ->map(fn(int $x) => $x ** 2)
    ->reduce(fn(int $sum, int $x) => $sum + $x, 0)
;

然后,有可迭代的对象 FluentIterable

它基于处理几乎无限的集合而不会耗尽所有内存并最终出错的想法。因此,执行在很大程度上依赖于 CPU,其余部分取决于您的代码。由于它将在运行时执行,因此项目将逐个通过运算符,而不是在生成每个步骤完成后才进行下一步。这很有用,因为您将逐个接收正在处理的项目,而不是接收整个大型结果块。您可以想象读取和解析一个非常大的文件,同时仅对某些行进行操作,而无需将整个文件加载到内存中,只需加载您感兴趣的部分。

<?php

use JumpIfBelow\Arrays\FluentIterable;

// we are creating a sum of a column in a CSV file
$sum = FluentIterable
    ::from(function () {
        $f = fopen('myfile.csv', 'r');

        while (($line = fgetcsv($f)) !== false) {
            yield $line;
        }

        fclose($f);
    })
    ->map(fn (array $row) => $row[1])
    ->filter(fn (int $value) => $value >= 0)
    ->reduce(fn (int $sum, int $value) => $sum + $value, 0)
;
// all of the code, from file opening and operations will only be executed when reduce is started
// once reduce is done, it will close the file handler

请注意,将 FluentIterator 转换为另一个迭代器是 OperatorInterface 处理的操作。因此,当使用 map 操作时,您实际上只调用了内部的 MapOperator。API 是公开的,以便在需要时使用您自己的运算符。如果我们回顾上面的示例,我们可以这样编写

<?php

use JumpIfBelow\Arrays\FluentIterable;
use JumpIfBelow\Arrays\IterableOperator\{
    FilterOperator,
    MapOperator,
};

$sum = FluentIterable
    ::from(function () {
        $f = fopen('myfile.csv', 'r');

        while (($line = fgetcsv($f)) !== false) {
            yield $line;
        }

        fclose($f);
    })
    ->apply(
        MapOperator::with(fn (array $row) => $row[1]),
        FilterOperator::with(fn (int $value) => $value >= 0),
    )
    ->reduce(fn (int $sum, int $value) => $sum + $value, 0)
;

它也足够强大,不会一次性生成所有内容。例如,这将从 range 调用开始耗尽内存

<?php

use JumpIfBelow\Arrays\FluentArray;

$array = FluentArray
    ::range(1, 2 ** 32 - 1)
    ->filter(fn (int $v): bool => $v >= 10 ** 7 && $v < 10 ** 8)
    ->slice(5, 10)
;

同时,您可以选择一个更大的集合,对其进行过滤,只获取其中的一部分,而无需真正进入整个集合。在这个例子中,我们将从0到2的64次方(如果您在64位操作系统上)进行操作。然后,仅过滤出介于1000万和1亿之间的值。之后,获取第5个值之后的值,并仅保留其中的10个。整个集合不会一次性生成,也不会超过10000014。

<?php

use JumpIfBelow\Arrays\FluentIterable;

$iterable = FluentIterable
    ::range(0, PHP_INT_MAX)
    ->filter(fn (int $v): bool => $v >= 10 ** 7 && $v < 10 ** 8)
    ->slice(5, 10)
;

不可变和可变方法

这应该只使用不可变引用,意味着调用任何修改内容的函数应返回一个包含修改数据的数组的新版本。旧引用保持不变。由于某些函数的行为方式,存在对此行为的例外。以下是一个方法列表

FluentArray

可变

  • pop
  • shift

不可变

  • arsort
  • asort
  • changeKeyCase
  • chunk
  • column
  • combine
  • countValues
  • diff
  • diffAssoc
  • diffKey
  • diffUassoc
  • diffUkey
  • fillKeys
  • filter
  • flip
  • intersect
  • intersectAssoc
  • intersectKey
  • intersectUassoc
  • intersectUkey
  • keys
  • krsort
  • ksort
  • map
  • merge
  • mergeRecursive
  • multisort
  • natcasesort
  • natsort
  • pad
  • push
  • rand
  • replace
  • replaceRecursive
  • reverse
  • rsort
  • shuffle
  • slice
  • sort
  • splice
  • uasort
  • udiff
  • udiffAssoc
  • udiffUassoc
  • uintersect
  • uintersectAssoc
  • uintersectUassoc
  • uksort
  • unique
  • unshift
  • usort
  • values
  • walk
  • walkRecursive

ExtendedArray

可变

  • nextKey
  • nextValue
  • nextEntry

不可变

  • forEach
  • every
  • some
  • entry
  • findKey
  • findValue
  • quantiles
  • quantileKeys
  • quantileValues
  • flat
  • groupBy
  • indexBy
  • fetch
  • swap
  • with
  • fullMap
  • fullReduce
  • permutation
  • first
  • last

FluentIterable

可变

None

不可变

  • apply
  • forEach
  • map
  • filter
  • reduce
  • replace
  • slice
  • unique
  • indexBy
  • some
  • every
  • toArray