Domanda

In Python (e altri), è possibile in modo incrementale elaborare grandi volumi di dati utilizzando l'operatore 'resa' in una funzione. Quale sarebbe il modo simile di farlo in PHP?

Ad esempio, consente di dire in Python, se volevo leggere un potenzialmente file molto grande, ho potuto lavorare su ogni riga uno alla volta, in questo modo (questo esempio è artificioso, in quanto è fondamentalmente la stessa cosa come 'per linea 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

Quello che sto facendo in questo momento (in PHP) è che sto usando una variabile di istanza privato per tenere traccia dello stato, e agire di conseguenza ogni volta che la funzione viene chiamata, ma sembra che ci deve essere un modo migliore.

È stato utile?

Soluzione

PHP ha un equivalente diretto chiamato generatori .

Vecchio (pre PHP 5.5 risposta):

Purtroppo, non c'è una lingua equivalente. Il modo più semplice è quello di uno a quello che stai già facendo, o per creare un oggetto che utilizza variabili di istanza per mantenere lo stato.

C'è comunque una buona opzione se si desidera utilizzare la funzione in combinazione con il foreach-dichiarazione: SPL Iteratori . Essi possono essere utilizzati per realizzare qualcosa di molto simile a generatori Python.

Altri suggerimenti

C'è un RFC a https://wiki.php.net/rfc/generators indirizzamento proprio questo, che potrebbe essere incluso in PHP 5.5.

Nel frattempo, controlla questo proof-of-concept di un mans poveri "funzione di generatore", realizzata in 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";

I prototipi di tutto in Python prima di attuare in altre lingue, tra cui PHP. Ho finito per usare callback per ottenere quello che farebbe con la 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');

In questo modo ogni $data viene passato alla funzione di callback e si può fare quello che vuoi.

L'estensione @ risposta di Luiz - un altro modo fresco è quello di utilizzare funzioni anonime:

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;

non ci può essere un operatore equivalente, ma il seguente codice è equivalente in funzione e ambientale:

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

Che sembra circa la destra. Mi dispiace, non l'ho testato.

La stessa frase 'resa' esiste ora in PHP 5.5:

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top