Question

En Python (et d'autres), vous pouvez traiter progressivement de grands volumes de données en utilisant l'opérateur « rendement » dans une fonction. Quelle serait la même façon de le faire en PHP?

Par exemple, permet de dire en Python, si je voulais lire un fichier potentiellement très important, je pourrais travailler sur chaque ligne un à la fois comme si (cet exemple est contraint, car il est fondamentalement la même chose que « pour ligne dans 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

Ce que je fais en ce moment (en PHP) est que je utilise une variable d'instance pour garder une trace de l'Etat, et agir en conséquence chaque fois que la fonction est appelée, mais il semble que il doit y avoir une meilleure façon.

Était-ce utile?

La solution

PHP a un équivalent direct appelé .

Old (pre php 5.5 réponse):

Malheureusement, il n'y a pas d'équivalent linguistique. La meilleure façon est soit à ce que vous faites déjà, ou pour créer un objet qui utilise les variables d'instance pour maintenir l'état.

Il y a cependant une bonne option si vous voulez utiliser la fonction conjointement avec le foreach-déclaration: SPL itérateurs . Ils peuvent être utilisés pour réaliser quelque chose tout à fait semblable à des générateurs de python.

Autres conseils

Il y a un rfc https://wiki.php.net/rfc/generators aDRESSAGE juste que, ce qui pourrait être inclus dans PHP 5.5.

Dans le même temps, consultez cette preuve de concept d'un pauvre mans « fonction de générateur » mis en œuvre dans le domaine utilisateur.

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";

Je prototype tout en Python avant la mise en œuvre dans toutes les autres langues, y compris PHP. Je fini par utiliser callbacks pour obtenir ce que je veux avec le 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');

De cette façon, chaque $data est passé à la fonction de rappel et vous pouvez faire ce que vous voulez.

L'extension @ la réponse de Luiz - une autre façon cool est d'utiliser les fonctions anonymes:

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;

Il ne peut pas être un opérateur équivalent, mais le code suivant est équivalent en fonction et les frais généraux:

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') ) {
  // ...
}

Cela semble juste. Désolé, je ne l'ai pas testé.

La même phrase 'rendement' existe maintenant sur PHP 5.5:

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top