根据documentation,如果可能的话,可以委托任何Traversable对象生成。但是,我看到了yield from {a Generator instance}
和yield from {an Iterator instance}
之间的区别。
这段代码具有相同的Iterator
和Generator
:它们提供了“ 1、2、3”序列:
$iterator = new class implements \Iterator {
private $values = [1, 2, 3];
public function current()
{
return current($this->values);
}
public function next()
{
next($this->values);
}
public function key()
{
return key(this->values);
}
public function valid()
{
return current($this->values) !== false;
}
public function rewind()
{
reset($this->values);
}
};
$generator = (function ()
{
yield 1;
yield 2;
yield 3;
})();
但是当我尝试yield from
时,我得到了不同的结果。让我们玩这个功能:
function skipOne(\Iterator $iterator)
{
$iterator->next();
yield from $iterator;
}
有了发电机,一切都会按预期进行:
foreach (skipOne($generator) as $value) {
var_dump($value);
}
Output:
int(2)
int(3)
但是使用Iterator不会跳过第一个数字:
foreach (skipOne($iterator) as $value) {
var_dump($value);
}
Output:
int(1)
int(2)
int(3)
我发现yield from
运算符由于某种原因导致了$iterator->rewind()
的调用。我在做什么错了?
答案 0 :(得分:2)
您没有做任何错误。
yield from
会尝试rewind
可以倒带的东西,但是生成器却不能,它们只会前进。
这就是您的代码在第一个示例中能够按预期工作的原因,而不是在第二个示例中。
您可以将$iterator
包裹在生成器中,并得到相同的结果。
$wrappedIterator = (function($iterator) {
foreach ($iterator as $value) {
yield $value;
}
})($iterator);
您只是发电机的一次使用性质的有益的“优势”,因为它们只能前进而不能倒退。
例如,foreach
也会发生同样的情况,因为它还会尝试倒带正在播放的内容。
如果你只是做
$iterator->next();
foreach ($iterator as $value) {
var_dump($value);
}
您还将获得:
int(1)
int(2)
int(3)
在两种情况下(yield from
和foreach
),迭代器都将被倒退。
尽管您尝试过
$generator->next();
foreach ($generator as $value) {
var_dump($value);
}
您将收到一个致命错误。
在您的情况下,由于yield from
不能倒退对象的感情,它不会抱怨,并且好像什么也没发生一样继续前进,因此可能会造成混淆。
您可以通过选中this来轻松验证行为。
答案 1 :(得分:1)
正如其他人所说,yield from
将呼叫$iterator->rewind()
。
生成器不能倒带,因为实现了rewind()
不能执行任何操作。
生成器是仅向前的迭代器,一旦迭代开始就不能重新生成。这也意味着同一生成器不能多次迭代:需要再次调用生成器函数来重建生成器。
因此,如果您希望具有与Generator相同的行为,则有两个选择。
只需将rewind()
的{{1}}方法保留为空,然后将所有倒带代码移至迭代器的构造函数。
Iterator
这将使迭代器的可重用性降低,同时也使外部代码不清楚为什么迭代器的行为不像常规迭代器。因此,请谨慎使用此选项。
class MyIterator implements \Iterator
{
public function __construct()
{
// put rewind code here
}
public function rewind()
{
// Do nothing
}
}
NoRewindIterator
是SPL iterator的另一个decorates类。
NoRewindIterator
除了$noRewindIterator = new \NoRewindIterator($iterator);
方法外,它的行为与给定的$iterator
相同。调用rewind()
时,什么都不会发生。
这样做的优点是允许您编写普通的可倒带迭代器,同时还可以进行部分迭代。
您的$noRewindIterator->rewind()
函数如何使用skipOne()
:
NoRewindIterator
答案 2 :(得分:0)
PHP documentation说The outer generator will then yield all values from the inner generator, object, or array until that is no longer valid, after which execution will continue in the outer generator.
All
是此处关键字之一。 PHP source code也证明了这一点。