bonami / collections
以不可变性和函数式方法为重点的集合库
Requires
- php: >=7.4|^8.0
- ext-json: *
Requires (Dev)
- bonami/phpstan-collections: ^0.4.2
- ergebnis/composer-normalize: ^2.0.2
- phpstan/phpstan: =1.8.2
- phpunit/phpunit: ^9.4.2
- slevomat/coding-standard: ^6.4.1
- squizlabs/php_codesniffer: ^3.5.0
Suggests
- bonami/phpstan-collections: Allow proper type resolving with phpstan
- 0.5.x-dev
- dev-master / 0.5.x-dev
- 0.5.2
- 0.5.1
- 0.5.0
- 0.4.x-dev
- 0.4.8
- 0.4.7
- 0.4.6
- 0.4.5
- 0.4.4
- 0.4.3
- 0.4.2
- 0.4.1
- 0.4.0
- 0.3.x-dev
- 0.3.11
- 0.3.10
- 0.3.9
- 0.3.8
- 0.3.7
- 0.3.6
- 0.3.5
- 0.3.4
- 0.3.3
- 0.3.2
- 0.3.1
- 0.3
- 0.2
- 0.1
- dev-jm-upgrade-deps
- dev-json-serializable
- dev-fix-missing-traversable-use
- dev-jm-linked-list
- dev-jm-applicative-via-product
- dev-jm-wip-foldable-type-class
- dev-jm-product
- dev-docs/adjust-try-safe-docs
- dev-jm-either-doc
- dev-jm-map-flatmap
- dev-jt-bench
This package is auto-updated.
Last update: 2024-09-09 14:29:54 UTC
README
目录
动机
为什么还需要为PHP创建另一个集合库?原生的PHP数组或SPL结构(如SplFixedArray或SplObjectStorage等)是可变的,并且具有非常奇怪的用户界面和行为。它们通常一次代表多个数据结构(例如,SplObjectStorage代表集合和映射),它们的接口是为经典的过程式方法设计的。
我们尝试设计结构的接口,使其专注于声明式方法,利用函数式编程。为了更高的安全性,我们设计了结构使其不可变(我们也有一些可变结构,因为有时出于性能原因这是必要的)
所有代码都设计为使用 phpstan 泛型 来确保类型安全。
展示代码!
代码示例胜过千言万语,所以这里有一些简单的示例
过滤Person DTO并提取一些信息
use Bonami\Collection\ArrayList; class Person { public function __construct( private readonly string $name, private readonly int $age ) {} } $persons = ArrayList::of(new Person('John', 31), new Person('Jacob', 22), new Person('Arthur', 29)); $names = $persons ->filter(fn (Person $person): bool => $person->age <= 30) ->sort(fn (Person $a, Person $b): int => $a->name <=> $b->name) ->map(fn (Person $person): string => $person->name) ->join(";"); // $names = "Arthur;Jacob"
生成组合
use Bonami\Collection\ArrayList; $colors = ArrayList::fromIterable(['red', 'green', 'blue']); $objects = ArrayList::fromIterable(['car', 'pencil']); $coloredObjects = ArrayList::fromIterable($colors) ->flatMap(fn (string $color) => $objects->map(fn (string $object) => "{$color} {$object}")) // $coloredObjects = ArrayList::of('red car', 'red pencil', 'green car', 'green pencil', 'blue car', 'blue pencil')
使用 提升操作符 生成组合
use Bonami\Collection\ArrayList; $concat = fn (string $first, string $second) => "{$first} {$second}"; $coloredObjects = ArrayList::lift2($concat)($colors, $objects);
字符频率分析
use Bonami\Collection\ArrayList; use Bonami\Collection\Map; use function Bonami\Collection\identity; use function Bonami\Collection\descendingComparator; function frequencyAnalysis(string $text): Map { $chars = preg_split('//u', $text, -1, PREG_SPLIT_NO_EMPTY); return ArrayList::fromIterable($chars) ->groupBy(identity()) ->mapValues(fn (ArrayList $group): int => $group->count()); } $text = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras nec mi rhoncus, dignissim tortor ac,' . ' aliquam metus. Maecenas non hendrerit tellus. Nam molestie augue ac lectus cursus consequat. Nunc ' . 'ultrices metus sit amet nulla blandit lacinia. Nam vestibulum ultrices mollis. Morbi consequat ante non ' . 'ornare lobortis. Nullam enim mauris, tempus quis auctor eu, condimentum dignissim nunc. Integer dapibus ' . 'dolor eu nisl euismod sagittis. Phasellus magna ante, pharetra eget nisi vehicula, elementum lacinia dui. ' . 'Aliquam semper at eros a sodales. In a rhoncus sapien. Integer blandit volutpat nisl. Donec vitae massa eget ' . 'mauris dignissim cursus nec et erat. Suspendisse consectetur ac quam sit amet pretium.'; // top ten characters by number of occurrences $top10 = frequencyAnalysis($text) ->sortValues(descendingComparator()) ->take(10);
特性
结构
\Bonami\Collection\ArrayList
- 一个不可变(非关联)数组包装器,旨在进行顺序处理。\Bonami\Collection\Map
- 一个不可变的键值结构。它可以包含任何类型的对象作为键(有一些限制,请参阅文档中的更多信息)。\Bonami\Collection\Mutable\Map
- Map的可变版本。\Bonami\Collection\LazyList
- 任何可迭代结构的包装器。它通过内部使用yield来利用惰性。它可以显著节省内存。\Bonami\Collection\Enum
- 不是一个集合,但与库的其余部分具有很好的协同作用。用于定义封闭枚举。提供获取给定枚举值的补集列表等有趣的方法。\Bonami\Collection\EnumList
- 枚举列表,扩展ArrayList\Bonami\Collection\Option
- 用于表示可能具有值和可能不具有值的不可变结构。它提供了一种安全(函数式)的方法来处理空指针错误。\Bonami\Collection\TrySafe
- 用于表示在过程中生成值或错误的不可变结构。它提供了一种安全(函数式)的方法来处理错误,而不产生副作用。\Bonami\Collection\CurriedFunction
- 表示单参数函数。它可以创建多参数函数的柯里化版本,这对于某些函数式编程组合模式更有利。
类型安全
我们使用 phpstan 注解来提高类型安全性,并利用泛型。为了更好的类型解析,我们创建了一个可选依赖项 phpstan-collections,我们强烈建议您如果使用phpstan,则安装它。它修复了一些类型解析,特别是对于后期静态绑定。
遍历
您可能会遇到这样的情况:使用映射函数将列表映射到返回包装在 Option
中的值,但您更希望得到未包装的值。这时,traverse
方法就派上用场了。
use Bonami\Collection\ArrayList; use Bonami\Collection\Option; $getUserNameById = function(int $id): Option { $userNamesById = [ 1 => "John", 2 => "Paul", 3 => "George", 4 => "Ringo", ]; return Option::fromNullable($userNamesById[$id] ?? null); }; print Option::traverse(ArrayList::fromIterable([1, 3, 4]), $getUserNameById); // Some([John, Paul, Ringo])
将结果与我们的老朋友 ArrayList::map
的用法进行比较。
use Bonami\Collection\ArrayList; use Bonami\Collection\Option; $getUserNameById = function(int $id): Option { $userNamesById = [ 1 => "John", 2 => "Paul", 3 => "George", 4 => "Ringo", ]; return Option::fromNullable($userNamesById[$id] ?? null); }; print ArrayList::fromIterable([1, 3, 4]) ->map($getUserNameById); // [Some(John), Some(George), Some(Ringo)]
您注意到区别了吗?在这里我们有包含字符串的选项列表,而在第一个代码示例中,我们有包含字符串列表的选项。
所以 traverse
允许我们将 Options
列表转换为包含未包装值的 Option
列表。而且,您猜怎么着——像往常一样,None
会毁掉一切。
use Bonami\Collection\ArrayList; use Bonami\Collection\Option; $getUserNameById = function(int $id): Option { $userNamesById = [ 1 => "John", 2 => "Paul", 3 => "George", 4 => "Ringo", ]; return Option::fromNullable($userNamesById[$id] ?? null); }; print Option::traverse(ArrayList::fromIterable([1, 3, 666]), $getUserNameById); // None
traverse
方法的使用不仅限于 Option
类。它适用于任何可应用的对象,所以它也适用于 TrySafe
、ArrayList
和 LazyList
(Failure
和空列表实例的行为与 None
相同)。
许可证
本软件包在 MIT 许可证 下发布。
贡献
如果您希望为项目做出贡献,请阅读 CONTRIBUTING 指南。