stubbles / sequence
流式传输迭代器。
Requires
- php: ^8.2
- ext-json: *
Requires (Dev)
- bovigo/assert: ^8.0
- bovigo/callmap: ^8.0.1
- phpunit/phpunit: ^10.5
This package is auto-updated.
Last update: 2024-08-31 15:12:51 UTC
README
流式传输迭代器。
构建状态
安装
stubbles/sequence 以 Composer 包的形式分发。要将它作为包的依赖项安装,请使用以下命令
composer require "stubbles/sequence": "^10.0"
需求
stubbles/sequence 至少需要 PHP 8.0。
简介
序列操作分为中间操作和终端操作,并通过管道组合。一个管道由一个源(例如集合、数组、生成器函数或I/O通道)组成;接着是零个或多个中间操作,如 Sequence::filter()
或 Sequence::map()
;以及一个终端操作,如 Sequence::each()
或 Sequence::reduce()
。
中间操作返回一个新的序列。它们始终是懒加载的;执行中间操作如 Sequence::filter()
并不会实际进行过滤,而是创建一个新的序列,当遍历时,包含与给定谓词匹配的初始流中的元素。直到执行管道的终端操作,才会开始遍历管道源。
终端操作,如 Sequence::each()
或 Sequence::reduce()
,可以遍历序列以产生结果或副作用。终端操作执行后,管道被认为是消耗掉的,不能再使用;如果需要再次遍历相同的数据源,必须回到数据源以获取一个新的序列。在几乎所有情况下,终端操作都是急切的,在返回之前完成对数据源的遍历和管道的处理。只有终端操作 Sequence::getIterator()
不是;这作为一个“逃生舱”,以便在现有操作不足以完成任务的情况下,允许任意客户端控制的管道遍历。
创建一个序列
Sequence::of($elements)
创建给定 $elements
的序列,可以是 \Traversable
或数组。
自版本 8.1 以来,可以以以下方式创建序列
- 无参数:等价于
Sequence::of([])
- 一个参数是
Sequence
的实例:返回此序列 - 一个参数是
array
或\Traversable
:此序列 - 一个参数不是上述任何一种:等价于
Sequence::of([$element])
- 两个或多个参数:参数列表的序列
Sequence::infinite($seed, callable $operation)
创建一个无限序列。使用 $seed
可以指定初始值,而 $operation
必须是可调用的,它接受当前值并生成下一个值。
警告:在无限序列上调用终端操作会导致无限循环,试图计算终端值。在调用终端操作之前,应该通过 Sequence::limit()
限制序列。或者,您也可以遍历序列本身,并在需要时停止迭代。
Sequence::generate($seed, callable $operation, callable $validator)
创建一个在处理过程中生成值的序列。
序列在提供的验证器首次返回 false
时结束。验证器接收两个值:最后生成的值和已生成的值的数量。
以下示例生成一个数组,其中以 $start 作为第一个值,每个后续值递增 2,数组中的值的数量最多为 100 或达到 PHP_INT_MAX。
Sequence::generate( $start, function($previous) { return $previous + 2; }, function($value, $invocations) { return $value < (PHP_INT_MAX - 1) && 100 >= $invocations; } )->values();
中间操作
limit($n)
限制序列到前 n 个元素,即当达到第 n 个元素时停止迭代。
Sequence::of([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])->limit(3)->data();
结果: [1, 2, 3]
skip($n)
跳过序列的前 n 个元素。
Sequence::of([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])->skip(10)->data();
结果: [11]
filter(callable $predicate)
返回一个新序列,包含匹配给定谓词的元素。给定的谓词接收一个值,必须返回 true 来接受该值,或返回 false 来拒绝该值。
Sequence::of([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) ->filter(function($value) { return $value % 2 === 0;}) ->data();
结果: [2, 4, 6, 8, 10]
map(callable $valueMapper, callable $keyMapper = null)
返回一个新序列,该序列使用给定的映射器映射每个元素。
Sequence::of([1, 2, 3, 4])->map(function($e) { return $e * 2; })->data();
结果: [2, 4, 6, 8]
mapKeys(callable $keyMapper)
返回一个新序列,该序列使用给定的映射器映射每个键。
Sequence::of([1, 2, 3, 4])->mapKeys(function($e) { return $e * 2; })->data();
结果: [0 => 1, 2 => 2, 4 => 3, 6 => 4]
append($other)
追加任何值,创建一个新的合并序列。
如果给定的 $other 不是可迭代的,则简单地将其作为最后一个元素追加到新序列中。
Sequence::of([1, 2])->append([3, 4]); // results in new sequence with [1, 2, 3, 4]
peek(callable $valueConsumer, callable $keyConsumer = null)
允许消费者在应用任何进一步操作之前接收值。
您可以使用它来在应用任何进一步操作之前检查值和键。这在需要调试序列内容时特别有用。
Sequence::of([1, 2, 3, 4])->peek('var_dump');
终端操作
each(callable $consumer)
对每个元素调用消费者并返回调用次数。
消费者接收元素作为第一个值,键作为第二个值
Sequence::of(['foo' => 'bar'])->each( function($element, $key) { // do something with $element and $key } );
键是可选的,可以省略
Sequence::of(['foo' => 'bar'])->each( function($element) { // do something with $element and $key } );
可以通过从消费者返回 false
来停止迭代。以下示例在达到元素 2 时停止
Sequence::of([1, 2, 3, 4])->each( function($element) { echo $element . "\n"; return (2 <= $element); } );
first()
返回序列的第一个元素。
echo Sequence::of(['foo', 'bar', 'baz'])->first(); // displays 'foo'
reduce(callable $accumulate = null, $identity = null)
将序列的所有元素归约到单个值。给定的可调用函数将接收两个值:当前归约值(在第一次调用时是 $identity
的值)和当前元素。它需要从这两个值中计算并返回一个新值,该值成为新的身份值,并在处理完最后一个元素后从 reduce()
返回。
Sequence::of([1, 2, 3, 4])->reduce(function($identity, $b) { return $identity + $b; });
如果没有提供可调用函数,将返回 stubbles\sequence\Reducer
的一个实例,该实例提供了一些常见归约操作的便捷方法。
reduce()->toSum(callable $summer = null)
将序列归约到所有元素的总和。默认情况下,假设序列由数字组成,并简单地将它们一个接一个地相加。
Sequence::of([1, 2, 3, 4])->reduce()->toSum();
如果序列由其他类型组成,则可以传递一个可调用函数来计算总和。该可调用函数必须期望两个值:到目前为止计算的总和和一个单一元素。返回值必须是带有给定元素的新的总和。
Sequence::of(['a', 'b', 'c', 'd'])->reduce()->toSum( function($sum, $element) { return $sum + ord($element); } );
reduce()->toMin(callable $min = null)
将序列归约到最小元素。默认情况下,假设序列由数字组成。
Sequence::of([1, 2, 3, 4])->reduce()->toMin();
如果序列由其他类型组成,则可以传递一个可调用函数来计算最小值。该可调用函数必须期望两个值:到目前为止找到的最小值(在第一次调用时为 null
)和一个单一元素。返回值必须是两个参数中的较小者。
Sequence::of(['a', 'b', 'c', 'd'])->reduce()->toSum( function($smallest, $element) { return (null === $smallest || ord($element) < ord($smallest)) ? $element : $smallest; } );
reduce()->toMax(callable $max = null)
将序列归约到最大元素。默认情况下,假设序列由数字组成。
Sequence::of([1, 2, 3, 4])->reduce()->toMax();
如果序列包含其他类型,可以传递一个可调用来计算最大值。可调用必须期望两个值:迄今为止找到的最大值(在第一次调用时为null
)和单个元素。返回值必须是两个参数中较大的一个。
Sequence::of(['a', 'b', 'c', 'd'])->reduce()->toMax( function($greatest, $element) { return (null === $greatest || ord($element) > ord($greatest)) ? $element : $greatest; } );
collect(Collector $collector = null)
将所有元素收集到由给定收集器定义的结构中。
收集器将元素累积到结构中,可选地将结果转换为最终表示形式。
如果没有提供收集器,将返回stubbles\sequence\Collectors
的实例,它为一些常见的收集器操作提供了便捷方法。
collect()->inList()
以数组形式返回序列的值。
Sequence::of(['foo' => 'bar', 'dummy' => 'baz'])->collect()->inList(); // returns ['bar', 'baz']
collect()->inMap(callable $selectKey = null, callable $selectValue = null)
返回具有键和值的关联数组的序列数据。$selectKey
可调用将用于确定新映射中的值的关键字,而$selectValue
将用于确定值。如果它们被省略,将使用源元素中的键和值。
$people= [ 1549 => new Employee(1549, 'Timm', 'B', 15), 1552 => new Employee(1552, 'Alex', 'I', 14), 6100 => new Employee(6100, 'Dude', 'I', 4) ]; $employees = Sequence::of($people)->collect()->inMap( function(Employee $e) { return $e->id(); }, function(Employee $e) { return $e->name(); } ); // results in [1549 => 'Timm', 1552 => 'Alex', 6100 => 'Dude']
collect()->inPartitions(callable $predicate, Collector $base = null)
根据给定的谓词将元素分为两个分区。
$timm = new Employee(1549, 'Timm', 'B', 15); $alex = new Employee(1552, 'Alex', 'I', 14); $dude = new Employee(6100, 'Dude', 'I', 4); $employees = Sequence::of([$timm, $alex, $dude])->collect()->inPartitions( function(Employee $e) { return $e->years() > 10; } ); // results in [true => [$timm, $alex], false => [$dude]]
第二个参数可以用来影响实际分区值。
$timm = new Employee(1549, 'Timm', 'B', 15); $alex = new Employee(1552, 'Alex', 'I', 14); $dude = new Employee(6100, 'Dude', 'I', 4); $employees = Sequence::of([$timm, $alex, $dude])->collect()->inPartitions( function(Employee $e) { return $e->years() > 10; }, Collector::forAverage(function(Employee $e) { return $e->years(); }) ); // results in [true => 14.5, false => 4]
collect()->inGroups(callable $classifier, Collector $base = null)
根据给定的谓词将元素分为两个分区。
$timm = new Employee(1549, 'Timm', 'B', 15); $alex = new Employee(1552, 'Alex', 'I', 14); $dude = new Employee(6100, 'Dude', 'I', 4); $employees = Sequence::of([$timm, $alex, $dude])->collect()->inGroups( function(Employee $e) { return $e->department(); } ); // results in ['B' => [$timm], 'I' => [$alex, $dude]]
第二个参数可以用来影响实际的组值。
$timm = new Employee(1549, 'Timm', 'B', 15); $alex = new Employee(1552, 'Alex', 'I', 14); $dude = new Employee(6100, 'Dude', 'I', 4); $employees = Sequence::of([$timm, $alex, $dude])->collect()->inGroups( function(Employee $e) { return $e->department(); }, Collector::forSum(function(Employee $e) { return $e->years(); }) ); // results in ['B' => 15, 'I' => 18]
collect()->byJoining($delimiter = ', ', $prefix = '', $suffix = '', $keySeparator = null)
将所有元素连接成一个字符串。
$timm = new Employee(1549, 'Timm', 'B', 15); $alex = new Employee(1552, 'Alex', 'I', 14); $dude = new Employee(6100, 'Dude', 'I', 4); $employees = Sequence::of([$timm, $alex, $dude]) ->map(function(Employee $e) { return $e->name(); }) ->collect() ->byJoining(); // results in 'Timm, Alex, Dude'
当提供$keySeparator
时,也将包含键。
$timm = new Employee(1549, 'Timm', 'B', 15); $alex = new Employee(1552, 'Alex', 'I', 14); $dude = new Employee(6100, 'Dude', 'I', 4); $employees = Sequence::of([1549 => $timm, 1552 => $alex, 6100 => $dude]) ->map(function(Employee $e) { return $e->name(); }) ->collect() ->byJoining(', ', '(', ')', ':'); // results in '(1549:Timm, 1552:Alex, 6100:Dude)'
count()
返回序列中的元素数量。
echo Sequence::of(['foo', 'bar', 'baz'])->count(); // displays 3
由于序列也是\Countable
的实例,它也可以与PHP的本地count()
函数一起使用。
echo count(Sequence::of(['foo', 'bar', 'baz'])); // displays 3
values()
以数组形式返回序列的值,是collect()->inList()
的快捷方式。
Sequence::of(['foo' => 'bar', 'dummy' => 'baz'])->values(); // returns ['bar', 'baz']
data()
以关联数组形式返回序列数据,是collect()->inMap()
的快捷方式。
Sequence::of(['foo' => 'bar', 'dummy' => 'baz'])->data(); // returns ['foo' => 'bar', 'dummy' => 'baz']
使用bovigo/assert进行序列验证
自版本8.0.0起可用
如果您在单元测试中使用bovigo/assert进行断言,stubbles/sequence提供两个谓词,可以用来确保序列包含预期的数据
assertThat($yourSequence, Provides::values([1, 2, 3])); assertThat($yourSequence, Provides::data(['foo' => 1, 'bar' => 2, 'baz' => 3]));
两者都可用在stubbles\sequence\assert\Provides
类中。第一个仅检查值而不考虑键,而第二个也检查键。请注意,两者都检查确切的那些元素 - 如果序列包含更多的值,则谓词将失败。