PHP 可迭代到数组或可遍历

PHP iterable to array or Traversable

我很高兴 PHP 7.1 推出 the iterable pseudo-type

现在虽然这在循环这种类型的参数时很棒,但我不清楚当您需要将它传递给仅接受 array 的 PHP 函数时该怎么做或者只是一个 Traversable。例如,如果你想做一个 array_diff,而你的 iterable 是一个 Traversable,你将得到一个 array。相反,如果您调用一个带有迭代器的函数,如果 iterablearray.

,您将收到错误消息

是否有类似 iterable_to_array(不是:iterator_to_array)和 iterable_to_traversable 的东西?

我正在寻找一种解决方案,避免在我的函数中使用条件语句来解决这种差异,而不依赖于我定义自己的全局函数。

使用 PHP 7.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()一起使用就好了,但是因为它是一种语言结构,所以有点特殊。它也不保留关联数组中的键。

Is there something like iterable_to_array and iterable_to_traversable

只需将这些添加到项目的某处,它们不会占用很多 space 并为您提供所需的确切 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;
}

你可以先用iterator_to_array converting your variable to Traversable

$array = iterator_to_array((function() use ($iterable) {yield from $iterable;})());

转换方法取自this question下的评论。

这里是working demo

术语很容易混合

  • 可遍历
    • 迭代器(我把它看成一个具体的类型,就像用户定义的classA)
    • 迭代器聚合
  • iterable(这是一个伪类型,array或者traversable都可以接受)
  • array(这是一个具体类型,在需要 Iterator 类型的上下文中它不能与 Iterator 交换)
  • arrayIterator(可用于将数组转换为迭代器)

所以,这就是为什么如果函数 A(iterable $a){},那么它接受数组或实例的参数 traversable(Iterator、IteratorAggregate 都被接受,因为它是很明显,这两个 classes 实现了 Traversable。在我的测试中,传递 ArrayIterator 也有效。

如果参数指定了Iterator类型,传入数组会导致TypeError。

对于 "iterable to array" 情况,您似乎无法进行单一函数调用,您需要在代码中使用条件语句或像这样定义自己的函数:

function iterable_to_array( iterable $iterable ): array {
    if ( is_array( $iterable ) ) {
        return $iterable;
    }
    return iterator_to_array( $iterable );
}

对于 "iterable to Iterator" 情况,事情要复杂得多。使用 ArrayIterator 可以轻松地将数组转换为 TraversableIterator 个实例可以按原样返回。剩下 Traversable 个不是 Iterator 的实例。乍一看,您似乎可以使用 IteratorIterator,它需要一个 Traversable。但是 class 有漏洞并且在给它一个 IteratorAggregate 一个 returns 一个 Generator.

时不能正常工作

虽然我创建了一个包含两个转换函数的迷你库,但此问题的解决方案太长 post:

  • 函数iterable_to_iterator(可迭代的$iterable):迭代器
  • 函数iterable_to_array(可迭代的$iterable):数组

参见 https://github.com/wmde/iterable-functions

可以这样做:

$array = $iterable instanceof \Traversable ? iterator_to_array($iterable) : (array)$iterable;

对于 php >= 7.4,开箱即用:

$array = array(...$iterable);

https://3v4l.org/L3JNH

编辑: 仅在可迭代对象不包含字符串键时有效

我想扩展 Edwin Rodríguez 给出的答案,不幸的是我还没有足够的代表post对此作为评论。

比初始化数组然后使用 array_push 将迭代器内容加载到其中稍微简单一些,您可以简单地(对于某些用例)使用数组括号并组合展开;

$array = [...$iterable];

当您想要将干净的、可内联的语法传递给数组函数时,这非常方便。

编辑:我现在意识到这实际上与 Michael Petri 给出的答案相同,但是对于 php>=7.4 我认为短数组语法是首选。除非您需要遵循另有说明的特定风格指南