Comparaison des générateurs avec les objets Iterator

Le principal avantage des gĂ©nĂ©rateurs est leur simplicitĂ©. Moins de code doit ĂȘtre Ă©crit que lorsqu'il s'agit d'implĂ©menter une classe Iterator, et il est gĂ©nĂ©ralement plus lisible. Par exemple, la fonction et la classe suivante sont Ă©quivalentes :

<?php
function getLinesFromFile($fileName) {
if (!
$fileHandle = fopen($fileName, 'r')) {
return;
}

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

fclose($fileHandle);
}

// contre...

class LineIterator implements Iterator {
protected
$fileHandle;

protected
$line;
protected
$i;

public function
__construct($fileName) {
if (!
$this->fileHandle = fopen($fileName, 'r')) {
throw new
RuntimeException('Impossible d\'ouvrir le fichier : "' . $fileName . '"');
}
}

public function
rewind() {
fseek($this->fileHandle, 0);
$this->line = fgets($this->fileHandle);
$this->i = 0;
}

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);
}
}

Cependant, cette flexibilitĂ© a un coĂ»t : les gĂ©nĂ©rateurs sont des itĂ©rateurs n'allant que vers l'avant, et ils ne peuvent pas ĂȘtre rĂ©-initialisĂ©s une fois leur parcours commencĂ©. Cela signifie Ă©galement que le mĂȘme gĂ©nĂ©rateur ne peut pas ĂȘtre utilisĂ© Ă  plusieurs reprises : le gĂ©nĂ©rateur devra ĂȘtre reconstruit en appelant une nouvelle fois la fonction gĂ©nĂ©rateur.

add a note

User Contributed Notes 2 notes

up
107
mNOSPAMsenghaa at nospam dot gmail dot com ¶
12 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
23
sergeyzsg at yandex dot ru ¶
12 years 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);
}
?>