Comparing generators with Iterator objects

The primary advantage of generators is their simplicity. Much less boilerplate code has to be written compared to implementing an Iterator class, and the code is generally much more readable. For example, the following function and class are equivalent:

<?php
function getLinesFromFile($fileName) {
    if (!
$fileHandle fopen($fileName'r')) {
        return;
    }
 
    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() {
        
fseek($this->fileHandle0);
        
$this->line fgets($this->fileHandle);
        
$this->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);
    }
}
?>

This flexibility does come at a cost, however: generators are forward-only iterators, and cannot be rewound once iteration has started. This also means that the same generator can't be iterated over multiple times: the generator will need to be rebuilt by calling the generator function again.

Коментарии

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.
2013-06-20 22:45:42
http://php5.kiev.ua/manual/ru/language.generators.comparison.html
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);
}
?>
2014-06-08 15:32:03
http://php5.kiev.ua/manual/ru/language.generators.comparison.html

    Поддержать сайт на родительском проекте КГБ