【问题标题】:PHP iterable to array or TraversablePHP 可迭代到数组或 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

【问题讨论】:

    标签: php arrays iterator php-7 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

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

          【讨论】:

          【解决方案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,我相信短数组语法是首选。除非您需要遵循另有说明的特定样式指南

            【讨论】:

            猜你喜欢
            • 2017-11-03
            • 2018-12-01
            • 1970-01-01
            • 1970-01-01
            • 2016-12-25
            • 1970-01-01
            • 2016-05-23
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多