PHP 可迭代到数组或 Traversable
Posted
技术标签:
【中文标题】PHP 可迭代到数组或 Traversable【英文标题】:PHP iterable to array or Traversable 【发布时间】:2017-11-19 03:44:30 【问题描述】:我很高兴 php 7.1 引入了the iterable pseudo-type。
虽然这在循环这种类型的参数时很棒,但我不清楚当你需要将它传递给只接受array
或只接受Traversable
的 PHP 函数时该怎么做。例如,如果你想做一个 array_diff,你的iterable
是一个Traversable
,你会得到一个array
。相反,如果你调用一个接受迭代器的函数,如果iterable
是array
,你会得到一个错误。
有像iterable_to_array
(不是:iterator_to_array
)和iterable_to_traversable
这样的东西吗?
我正在寻找一种在我的函数中避免条件的解决方案,只是为了处理这种差异,并且不依赖于我定义自己的全局函数。
使用 PHP 7.1
【问题讨论】:
【参考方案1】:不确定这是您要搜索的内容,但这是最短的方法。
$array = [];
array_push ($array, ...$iterable);
我不太确定它为什么会起作用。只是我发现你的问题很有趣,我开始摆弄 PHP
完整示例:
<?php
function some_array(): iterable
return [1, 2, 3];
function some_generator(): iterable
yield 1;
yield 2;
yield 3;
function foo(iterable $iterable)
$array = [];
array_push ($array, ...$iterable);
var_dump($array);
foo(some_array());
foo(some_generator());
如果与函数 array()
一起使用会很好,但因为它是一种语言结构,所以有点特殊。它也不保留关联数组中的键。
【讨论】:
这是可行的,因为 array_push 接受可变数量的参数,并且您将带有 ... 的可迭代解包到参数列表中。由于需要在作用域中引入一个额外的变量然后对其进行变异,这对我来说不是一个可接受的解决方案。 @JeroenDeDauw 我知道 ... 展开它但在 array() 中的工作方式不同,所以它有些奇怪。我同意这不是一个可接受的解决方案,事实上,我认为这在 7.1 中是不可能的,但考虑到限制(不定义函数和使用条件),这是我发现的最接近的解决方案 如果生成器没有返回任何内容,则生成器将不起作用。然后出现错误 - 没有参数传递给array_push
,因为 ...
将解析为空。
由于 PHP 7.3 使用上面的代码不会为空的迭代抛出错误,array_push
可以只用第一个参数调用。【参考方案2】:
有没有像 iterable_to_array 和 iterable_to_traversable 这样的东西
只需将这些添加到您的项目中的某个地方,它们不会占用太多空间,并且可以为您提供所需的确切 API。
function iterable_to_array(iterable $it): array
if (is_array($it)) return $it;
$ret = [];
array_push($ret, ...$it);
return $ret;
function iterable_to_traversable(iterable $it): Traversable
yield from $it;
【讨论】:
【参考方案3】:可以这样做:
$array = $iterable instanceof \Traversable ? iterator_to_array($iterable) : (array)$iterable;
【讨论】:
【参考方案4】:术语很容易混合
可穿越 Iterator(我认为这是一个具体的类型,就像用户定义的 A 类) 迭代器聚合 可迭代(这是一个伪类型,数组或可遍历被接受) array(这是一个具体类型,在需要 Iterator 类型的上下文中,它不能与 Iterator 交换) arrayIterator(可用于将数组转换为迭代器)所以,这就是为什么如果函数 A(iterable $a),那么它接受数组或实例的参数 traversable(Iterator,IteratorAggregate 都被接受,因为很明显这两个类实现可遍历。在我的测试中,通过 ArrayIterator 也可以)。
如果参数指定了Iterator类型,传入数组会导致TypeError。
【讨论】:
【参考方案5】:对于 php >= 7.4,这可以很好地开箱即用:
$array = array(...$iterable);
见https://3v4l.org/L3JNH
编辑:仅在可迭代项不包含字符串键时才有效
【讨论】:
现在可以在 PHP 8.1 中使用字符串键:3v4l.org/UkdLD【参考方案6】:您可以先使用iterator_to_array
将变量转换为Traversable
:
$array = iterator_to_array((function() use ($iterable) yield from $iterable;)());
转换方法取自this question下的评论。
这里是working demo。
【讨论】:
你可以将你的可迭代属性转换成一个数组$array = (array) $iterable;
【参考方案7】:
对于“可迭代到数组”的情况,您似乎无法进行单个函数调用,并且您需要在代码中使用条件或定义自己的函数,如下所示:
function iterable_to_array( iterable $iterable ): array
if ( is_array( $iterable ) )
return $iterable;
return iterator_to_array( $iterable );
对于“可迭代到迭代器”的情况,事情要复杂得多。使用ArrayIterator
可以轻松地将数组转换为Traversable
。 Iterator
实例可以按原样返回。这留下了不是Iterator
的Traversable
实例。乍一看,您似乎可以使用IteratorIterator
,它需要Traversable
。但是,当给它一个返回 Generator
的 IteratorAggregate
时,该类存在错误并且无法正常工作。
虽然我创建了一个包含两个转换函数的迷你库,但这个问题的解决方案太长了,无法在此处发布:
function iterable_to_iterator(iterable $iterable): 迭代器 function iterable_to_array(iterable $iterable): 数组见https://github.com/wmde/iterable-functions
【讨论】:
【参考方案8】:我想扩展 Edwin Rodríguez 给出的答案,不幸的是我还没有足够的代表来发表评论。
初始化数组然后使用array_push将迭代器内容加载到其中稍微简单一些,您可以简单地(对于某些用例)使用数组括号并组合传播;
$array = [...$iterable];
当您想要一个干净的、可内联的语法传递给数组函数时,这非常方便。
编辑:我现在意识到这与 Michael Petri 给出的答案几乎相同,但是对于 php>=7.4,我相信短数组语法是首选。除非您需要遵循另有说明的特定样式指南
【讨论】:
这并没有提供问题的答案。一旦你有足够的reputation,你就可以comment on any post;相反,provide answers that don't require clarification from the asker。 - From Review以上是关于PHP 可迭代到数组或 Traversable的主要内容,如果未能解决你的问题,请参考以下文章