如何在没有 foreach 的情况下使用 PHP 生成器?

How to use PHP generators without foreach?

这是一个简单的 JavaScript 生成器(来自:http://blog.carbonfive.com/2013/12/01/hanging-up-on-callbacks-generators-in-ecmascript-6/

function* powGenerator() {
  var result = Math.pow(yield "a", yield "b");
  return result;
}

var g = powGenerator();
console.log(g.next().value);   // "a", from the first yield
console.log(g.next(10).value); // "b", from the second
console.log(g.next(2).value);  // 100, the result

我正在尝试对与 PHP 类似的东西建模,但这有点令人头疼。

<?php
function powGenerator() {
  return pow((yield 'a'), (yield 'b'));
}

在我继续之前,我在 PHP

中收到此错误

Fatal error: Generators cannot return values using "return"

好的,也许我会使用另一个 yield 来获取最终值? ...

<?php
function powGenerator() {
  yield pow((yield 'a'), (yield 'b'));
}

$g = powGenerator(); //=> Generator {#180}
echo $g->send(10);   //=> "b"
echo $g->send(2);    //=> 100

好的,所以我找回了我的价值,但这里有两个主要问题

  1. 我的 "a" 去了哪里? — 注意在 JS 示例中我能够访问 "a""b" 产生的值以及 100 最终结果。

  2. 生成器还没有完成! — 我必须再调用 send 来完成生成器

    $g->valid();   //=> true
    $g->send('?'); //=> null
    $g->valid();   //=> false
    

来自 PHP Generator::send

public mixed Generator::send ( mixed $value )

Sends the given value to the generator as the result of the current yield expression and resumes execution of the generator.

If the generator is not at a yield expression when this method is called, it will first be let to advance to the first yield expression before sending the value. As such it is not necessary to "prime" PHP generators with a Generator::next() call (like it is done in Python).

强调 "As such it is not necessary to "prime“PHP 发电机 Generator::next()”。好的,但这到底是什么意思?我不必像 JavaScript 示例那样 "prime" 它,但第一个产生的值也被吞没了。

任何人都可以解释如何使用 foreach 没有 的情况下单步执行生成器吗?

第一个 yield 的值并没有被吞掉,只是你没看过而已。

$g = powGenerator();
echo $g->current(); //a

然后你两次发送值并恢复执行,$g->valid() 在这之后是 true 因为你在第三次 yield 之后还没有恢复 - 生成器不是完成,可能还有更多事情要做。考虑:

function powGenerator() {
    yield pow((yield 'a'), (yield 'b'));
    echo "Okay, finishing here now!\n";
}

$g = powGenerator();
echo $g->current(), "\n"; //a
echo $g->send(10), "\n";  //b
echo $g->send(2), "\n";   //100
$g->next();               // Resumes execution of the generator,
                          // which prints its own message and completes.
var_dump($g->valid());    //false

这将输出:

a
b
100
Okay, finishing here now!
bool(false)

现在 PHP 7 你 可以 return from a generator.

function powGenerator() {
    return pow((yield 'a'), (yield 'b'));
    echo "This will never print.";
}

$g = powGenerator();
echo $g->current(), "\n"; //a
echo $g->send(10), "\n";  //b
echo $g->send(2), "\n";   // Prints just the newline, you're moving on
                          // to a return which you must get explicitly.
var_dump($g->valid());    // Generator complete, you're free to get the return.
echo $g->getReturn(), "\n";

输出:

a
b

bool(false)
100

至于在没有 foreach - Generator implements Iterator, so it's got appropriate methods to treat it as such: current, key, next, rewind, and valid 的情况下单步执行它们。需要注意的是,如果您在已经启动的生成器上调用它,rewind 将抛出异常。

执行此操作并演示 PHP 7 的新 generator delegation:

的示例
function letterGenerator() {
    yield from range('a', 'z');
}

$g = letterGenerator();

while ($g->valid()) {
    echo $g->current();
    $g->next();
}

输出:

abcdefghijklmnopqrstuvwxyz