为什么 'iterator_to_array' 给出的结果与 foreach 不同?

Why does 'iterator_to_array' give different results than foreach?

假设我有以下递归函数将数组转换为生成器:

function traverse(array $items): Generator {
    if (!empty($items)) {
        yield $items[0];

        array_shift($items);
        yield from traverse($items);
    }
}

运行 这个函数并通过 foreach 迭代生成器给出了预期的结果:

$values = traverse(['a', 'b', 'c', 'd', 'e']);

foreach ($values as $value) {
    echo $value;
}

// 'abcde' is echoed

但是,当我使用内置函数 iterator_to_array() 时,我得到以下结果:

$values = iterator_to_array(traverse(['a', 'b', 'c', 'd', 'e']));

foreach ($values as $value) {
    echo $value;
}

// Only 'e' is echoed

我希望这两段代码的行为相同,但事实并非如此。为什么他们的行为不同?

我是 运行 这个 PHP 8.1.

来自the manual

yield from does not reset the keys. It preserves the keys returned by the Traversable object, or array. Thus some values may share a common key with another yield or yield from, which, upon insertion into an array, will overwrite former values with that key.

A common case where this matters is iterator_to_array() returning a keyed array by default, leading to possibly unexpected results. iterator_to_array() has a second parameter use_keys which can be set to false to collect all the values while ignoring the keys returned by the Generator.

您的生成器每次都返回具有相同键的项目(因为它们在 array_shift 调用后被重置),因此 iterator_to_array 正在用下一个覆盖每个项目。这就是为什么您只看到返回的最后一个元素。

如果将 use_keys 参数作为 false 传递给函数,您将获得所需的行为,例如

$values = iterator_to_array(traverse(['a', 'b', 'c', 'd', 'e']), false);

https://3v4l.org/jWbt4