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。相反,如果你调用一个接受迭代器的函数,如果iterablearray,你会得到一个错误。

有像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 可以轻松地将数组转换为TraversableIterator 实例可以按原样返回。这留下了不是IteratorTraversable 实例。乍一看,您似乎可以使用IteratorIterator,它需要Traversable。但是,当给它一个返回 GeneratorIteratorAggregate 时,该类存在错误并且无法正常工作。

虽然我创建了一个包含两个转换函数的迷你库,但这个问题的解决方案太长了,无法在此处发布:

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的主要内容,如果未能解决你的问题,请参考以下文章

如何从一个可迭代的元组中填充两个(或更多)numpy 数组?

PHP - JSON 到数组,迭代并打印到字符串

ES6 扩展运算符

从 php 返回一个可迭代对象到 Ajax

JQuery怎么循环输出数组元素

在 Python 中将任何可迭代对象转换为数组