yield

(generator)

About me

  • I'm Tien
  • I'm a PHP/Drupal developer
  • I'm working at GO1

Outline

  • What is yield/generator?
  • Demo
    • Simple usage
    • Generator as object
    • Saving memory
    • Returning keys
    • Pseudo keys
    • Pass data to generator
  • Why generator?
  • How it work?
  • When to use generator?

Do you know generator?

  • return (80%)
  • interator (20%)

What is generator?

  • Is a Iterator.
  • Return once item in array at a time.
  • Look like a function, but behave like an iterator.

Iterator Interface

Iterator extends Traversable {
    /* Methods */
    abstract public mixed current ( void )
    abstract public scalar key ( void )
    abstract public void next ( void )
    abstract public void rewind ( void )
    abstract public boolean valid ( void )
}

How iterator work?

$it->rewind();

while ($it->valid())
{
    $key = $it->key();
    $value = $it->current();

    // ...

    $it->next();
}

Simple usage

function get_primes() {
    yield 2;

    $number = 3;
    while (true) {
        $prime = true;
        for ($i = 2; $i <= ($number / 2); $i++) {
            if (($number % $i) == 0) {
                $prime = false;
                break;
            }
        }
        if ($prime) {
            yield $number;
        }
        $number++;
    }
}

foreach (get_primes() as $number) {
    if ($number % 2 == 0) {
        print $number;
        break;
    }
}

Saving memory demo

// Test 1
$m = memory_get_peak_usage();
foreach (line_one_by_one('file1.txt') as $l);
echo memory_get_peak_usage() - $m, "\n"; //Outputs 7248

// Test 2
$m = memory_get_peak_usage();
foreach (all_lines('file1.txt') as $l);
echo memory_get_peak_usage() - $m, "\n"; // Outputs 25668

Pseudo keys

  • Just like array keys, but
    • Can be any PHP datatype
    • Can be duplicated

Peusedo keys

function get_items() {
    yield array('a' => 2) => 3;
    yield 'string' => 4;
    yield 1.234 => 5;
    yield function () {print 'hello';} => 6;
}

foreach (get_items() as $key => $value) {
    if (is_callable($key)) {
        call_user_func($key);
    }
    else if (is_array($key)) {
        print_r($key);
    }
    else {
        print $key;
    }

    print "=>";

    print $value . "\n";
}

Pass data to generator

$logFileName = __DIR__ . '/error.log';

function logger($logFileName) {
  $f = fopen($logFileName, 'a');
  while ($logentry = yield) {
    fwrite($f, (new DateTime())->format('Y-m-d H:i:s ') . $logentry . PHP_EOL);
  }
}

$logger = logger($logFileName);
for ($i = 0; $i < 12; ++$i) {
  $logger->send('Message #' . $i);
}

Generator as object

function xrange($min, $max)  {
    for ($i = $min; $i <= $max; $i++) {
        yield $i;
    }
}

$generator = xrange(0, 10);

while($generator->isValid()) {
    echo $generator->key() . ' => ' . $generator->current() . ', ';
    $generator->next();
}

// Print 0 => 0, 1 => 1 ...

Notes

  • Can not return values inside generator
  • Use 'return;' to terminate generator

Why generator?

  • Reduce memory use
  • Faster than iterating over an array
  • Cleaner and shorter code
  • Allows to start processing the first few values immediately

How it work?

When to use generator?

  • Process large array
  • Save memory
  • Write clean code

References

yield

By Tiến Võ Xuân