【问题标题】:How to use PHP generators without foreach?如何在没有 foreach 的情况下使用 PHP 生成器?
【发布时间】:2016-01-02 19:21:29
【问题描述】:

这是一个简单的 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 中遇到了这个错误

致命错误:生成器无法使用“return”返回值

好的,所以也许我会使用另一个收益来获得最终价值? ...

<?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" 产生的值以及 @ 987654330@最终结果。

  2. 生成器还没有完成! — 我必须再打电话给send 才能完成生成器

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

来自 PHP Generator::send

public mixed Generator::send ( mixed $value )

将给定值作为当前yield 表达式的结果发送到生成器,并继续执行生成器。

如果调用此方法时生成器不在yield 表达式中,则在发送值之前,它会先让其前进到第一个yield 表达式。因此,没有必要使用Generator::next() 调用“启动”PHP 生成器(就像在 Python 中所做的那样)。

强调“因此没有必要使用Generator::next() 来“启动”PHP 生成器”。好的,但这到底是什么意思?我不必像 JavaScript 示例那样“启动”它,但第一个产生的值也被吞没了。

谁能解释一下你是如何不使用使用foreach来单步调试生成器的?

【问题讨论】:

    标签: php generator


    【解决方案1】:

    第一个产生的值没有被吞掉,你只是从未看过它。

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

    然后您将两次发送值并恢复执行,$g-&gt;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 实现Iterator 的情况下单步执行它们,所以它有适当的方法来处理它:currentkeynextrewindvalid。需要注意的是,如果您在已经开始的生成器上调用 rewind 将引发异常。

    一个这样做的例子,也演示了 PHP 7 的新 generator delegation

    function letterGenerator() {
        yield from range('a', 'z');
    }
    
    $g = letterGenerator();
    
    while ($g->valid()) {
        echo $g->current();
        $g->next();
    }
    

    输出:

    abcdefghijklmnopqrstuvwxyz
    

    【讨论】:

    • 好吧,currentnext 的使用至少与现有的数组实现一致,但是不能指定生成器的结尾真的很愚蠢,imo。用于终止生成器的额外next() 调用使得围绕此构建您自己的程序化步进非常困难。该文档使 PHP 生成器听起来很棒,而不必像 Python 那样“启动”它们。无论如何,他们最终都会得到一个笨重的 API。经典 PHP。
    • PHP 7 中的return 确实解决了我的问题,但它并没有真正帮助我。 PHP 7 可能在很长一段时间内都不会稳定。我看不出现有的发电机怎么会如此短视。似乎只有将它们与 foreach 一起使用才实用。
    猜你喜欢
    • 2015-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-28
    • 2011-01-15
    • 1970-01-01
    • 2016-06-02
    • 2019-01-27
    相关资源
    最近更新 更多