Vergleich von Generatoren mit Iterator Objekten

Die erste Verbesserung von Generatoren ist ihre Einfachheit. Viel weniger überladener Code muss geschrieben werden als im Vergleich zur Implementierung einer Iterator Klasse und der Code ist generell einfacher zu lesen. Als Beispiel dienen folgende äquivalente Funktion und Klasse:

<?php
function leseZeilenVonDatei($dateiName) {
    if (!
$dateiHandle fopen($dateiName'r')) {
        return;
    }
 
    while (
false !== $zeile fgets($dateiHandle)) {
        
yield $zeile;
    }
 
    
fclose($dateiHandle);
}

// im Gegensatz zu...

class LineIterator implements Iterator {
    protected 
$dateiHandle;
 
    protected 
$zeile;
    protected 
$i;
 
    public function 
__construct($dateiName) {
        if (!
$this->dateiHandle fopen($dateiName'r')) {
            throw new 
RuntimeException('Kann Datei "' $dateiName '" nicht öffnen');
        }
    }
 
    public function 
rewind() {
        
fseek($this->dateiHandle0);
        
$this->zeile fgets($this->dateiHandle);
        
$this->0;
    }
 
    public function 
gueltig() {
        return 
false !== $this->zeile;
    }
 
    public function 
aktuelle() {
        return 
$this->zeile;
    }
 
    public function 
schluessel() {
        return 
$this->i;
    }
 
    public function 
naechste() {
        if (
false !== $this->zeile) {
            
$this->zeile fgets($this->dateiHandle);
            
$this->i++;
        }
    }
 
    public function 
__destruct() {
        
fclose($this->dateiHandle);
    }
}
?>

Diese Flexibilität kommt nicht ohne Kosten, nichts desto trotz: Generatoren sind forward-only Iteratoren, und können nicht zurückgesetzt werden, wenn sie einmal gestartet wurden. Das heißt ausserdem das der selbe Generator nicht mehrfach iteriert werden kann: der Generator muss entweder durch einen erneuten Aufruf der Funktion neu erstellt werden, oder muss durch das clone Schlüsselwort geklont werden.

add a note add a note

User Contributed Notes 3 notes

up
24
mNOSPAMsenghaa at nospam dot gmail dot com
2 years ago
This hardly seems a fair comparison between the two examples, size-for-size. As noted, generators are forward-only, meaning that it should be compared to an iterator with a dummy rewind function defined. Also, to be fair, since the iterator throws an exception, shouldn't the generator example also throw the same exception? The code comparison would become more like this:

<?php
function getLinesFromFile($fileName) {
    if (!
$fileHandle = fopen($fileName, 'r')) {
        throw new
RuntimeException('Couldn\'t open file "' . $fileName . '"');
    }

    while (
false !== $line = fgets($fileHandle)) {
       
yield $line;
    }

   
fclose($fileHandle);
}

// versus...

class LineIterator implements Iterator {
    protected
$fileHandle;

    protected
$line;
    protected
$i;

    public function
__construct($fileName) {
        if (!
$this->fileHandle = fopen($fileName, 'r')) {
            throw new
RuntimeException('Couldn\'t open file "' . $fileName . '"');
        }
    }

    public function
rewind() { }

    public function
valid() {
        return
false !== $this->line;
    }

    public function
current() {
        return
$this->line;
    }

    public function
key() {
        return
$this->i;
    }

    public function
next() {
        if (
false !== $this->line) {
           
$this->line = fgets($this->fileHandle);
           
$this->i++;
        }
    }

    public function
__destruct() {
       
fclose($this->fileHandle);
    }
}
?>

The generator is still obviously much shorter, but this seems a more reasonable comparison.
up
6
sergeyzsg at yandex dot ru
1 year ago
I think that this is bad generator example.
If user will not consume all lines then file will not be closed.

<?php
function getLinesFromFile($fileHandle) {
    while (
false !== $line = fgets($fileHandle)) {
       
yield $line;
    }
}

if (
$fileHandle = fopen($fileName, 'r')) {
   
/*
    something with getLinesFromFile
    */
   
fclose($fileHandle);
}
?>
up
2
sou at oand dot re
2 years ago
I think to be more similar the samples in the function, throw a new exception is better. But looking into "Generator syntax" session, you can see there this: "An empty return statement is valid syntax within a generator and it will terminate the generator.". By this point of view, we can imagine that this is just to exemplify an usage of the empty return.
To Top