代码之家  ›  专栏  ›  技术社区  ›  nilamo

PHP等价于Python的yield运算符

  •  19
  • nilamo  · 技术社区  · 15 年前

    在Python(和其他语言)中,您可以通过在函数中使用“yield”操作符以增量方式处理大量数据。在PHP中类似的方法是什么?

    例如,在Python中,如果我想读取一个可能非常大的文件,我可以一次处理每一行,就像这样(这个示例是人为的,因为它基本上与“For line in file_obj”相同):

    def file_lines(fname):
        f = open(fname)
        for line in f:
            yield line
        f.close()
    
    for line in file_lines('somefile'):
        #process the line
    

    我现在正在做的(在PHP中)是使用一个私有实例变量来跟踪状态,并在每次调用函数时相应地执行操作,但似乎必须有更好的方法。

    6 回复  |  直到 15 年前
        1
  •  18
  •   Rune Kaagaard    12 年前

    有一个rfc在 https://wiki.php.net/rfc/generators

    同时,看看这个在userland实现的穷人“发电机功能”的概念证明。

    namespace Functional;
    
    error_reporting(E_ALL|E_STRICT);
    
    const BEFORE = 1;
    const NEXT = 2;
    const AFTER = 3;
    const FORWARD = 4;
    const YIELD = 5;
    
    class Generator implements \Iterator {
        private $funcs;
        private $args;
        private $key;
        private $result;
    
        public function __construct(array $funcs, array $args) {
            $this->funcs = $funcs;
            $this->args = $args;
        }
    
        public function rewind() {
            $this->key = -1;
            $this->result = call_user_func_array($this->funcs[BEFORE], 
                                                 $this->args);
            $this->next();
        }
    
        public function valid() {
            return $this->result[YIELD] !== false;
        }
    
        public function current() {
            return $this->result[YIELD];
        }
    
        public function key() {
            return $this->key;
        }
    
        public function next() {
            $this->result = call_user_func($this->funcs[NEXT], 
                                           $this->result[FORWARD]);
            if ($this->result[YIELD] === false) {
                call_user_func($this->funcs[AFTER], $this->result[FORWARD]);
            }
            ++$this->key;
        }
    }
    
    function generator($funcs, $args) {
        return new Generator($funcs, $args);
    }
    
    /**
     * A generator function that lazily yields each line in a file.
     */
    function get_lines_from_file($file_name) {
        $funcs = array(
            BEFORE => function($file_name) { return array(FORWARD => fopen($file_name, 'r'));   },
            NEXT   => function($fh)        { return array(FORWARD => $fh, YIELD => fgets($fh)); },
            AFTER  => function($fh)        { fclose($fh);                                       },
        );
        return generator($funcs, array($file_name));
    }
    
    // Output content of this file with padded linenumbers.
    foreach (get_lines_from_file(__FILE__) as $k => $v) {
        echo str_pad($k, 8), $v;
    }
    echo "\n";
    
        2
  •  12
  •   Emil H    11 年前

    generators .

    旧的(php 5.5之前的答案):

    不幸的是,没有对等的语言。最简单的方法是要么修改您已经在做的事情,要么创建一个使用实例变量来维护状态的对象。

    SPL Iterators . 它们可以用来实现类似于python生成器的功能。

        3
  •  11
  •   Luiz Damim    15 年前

    在使用任何其他语言(包括PHP)实现之前,我用Python对所有内容进行了原型化。我最终使用回调来实现我想要的 yield .

    function doSomething($callback) 
    {
        foreach ($something as $someOtherThing) {
            // do some computations that generates $data
    
            call_user_func($callback, $data);
        }
    }
    
    function myCallback($input)
    {
        // save $input to DB 
        // log
        // send through a webservice
        // etc.
        var_dump($input);
    }
    
    
    doSomething('myCallback');
    

    每个人都这样 $data 被传递给回调函数,您可以执行您想要的操作。

        4
  •  3
  •   Slava V    13 年前

    function iterator($n, $cb)
    {
        for($i=0; $i<$n; $i++) {
            call_user_func($cb, $i);
        }
    }
    
    $sum = 0;
    iterator(10,
        function($i) use (&$sum)
        {
            $sum += $i;
        }
    );
    
    print $sum;
    
        5
  •  1
  •   Justin Johnson    15 年前

    可能没有等效运算符,但以下代码在功能和开销方面是等效的:

    function file_lines($file) {
      static $fhandle;
    
      if ( is_null($fhandle) ) {
        $fhandle = fopen($file, 'r');
    
        if ( $fhandle === false ) {
          return false;
        }
      }
    
      if ( ($line = fgets($fhandle))!== false ) {
        return $line;
      }
    
    
      fclose($fhandle);
      $fhandle = null;
    }
    
    while ( $line = file_lines('some_file') ) {
      // ...
    }
    

    看起来差不多。对不起,我还没有测试过。

        6
  •  1
  •   jhvaras    11 年前

    PHP 5.5上现在也有相同的句子“yield”:

    http://php.net/manual/en/language.generators.syntax.php