How to "simulate" a Generator in versions prior to PHP 5.5?

Asked

Viewed 60 times

3

From PHP 5.5 we can use yield instead of return functions and, with this, we create Generators.

The reasons for using the yield can be seen here What are the advantages of using a Generator (Yield) in PHP?

But I had a question: How could I implement something similar to a Generator in versions prior to PHP 5.5?

I would like an example where I could replace the function range, on account of memory savings.

Example PHP 5.5:

function xrange($start, $limit, $step = 1) {

    for ($i = $start; $i >= $limit; $i += $step) {
        yield $i;
    }
}

2 answers

3

What is a Generator function?

The Generator function is effectively a more compact and efficient way to write an Iterator. It allows you to define a function (your xrange), to calculate and return values while you are in loop:

foreach (xrange(1, 10) as $key => $value) {
    echo "$key => $value", PHP_EOL;
}

Exit:

0  =>  1 
1  =>  2 
... 
9  =>  10

Since there is no support for older versions, I can do it with "normal functions"??

Now you may wonder why not just use "Old and good native range " to reach that exit. And you’re right. The exit would be the same. The difference is how we get there.

When we use range, this will run the entire array of numbers in memory and return the entire array to the foreach which will then provide the values. In other words, the foreach operate in the matrix itself. The range and the foreach will only "chat" once. Think of it as getting a package at the post office. The courier will deliver the package to you and leave. And then you unwrap the whole package, taking out everything that is inside.

Examples:

<?php
// array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
foreach (range(0, 12) as $number) {
    echo $number;
}

// The step parameter
// array(0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100)
foreach (range(0, 100, 10) as $number) {
    echo $number;
}

// Usage of character sequences
// array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i');
foreach (range('a', 'i') as $letter) {
    echo $letter;
}
// array('c', 'b', 'a');
foreach (range('c', 'a') as $letter) {
    echo $letter;
}
?>
  • This answer does not seem to answer the question, I see in it no way to simulate a Generator, only an explanation about what is a.

1

To simulate a Generator in versions prior to PHP 5.5, it would be necessary to create a class that implements Iterator.

See simply how it would work:

class XRange implements Iterator
{
    protected $value = 0;

    protected $limit;

    protected $step;

    protected $initial;

    protected $key = 0;

    public function __construct($value, $limit, $step = 1)
    {
        $this->value = $this->initial = $value;

        $this->limit = $limit;

        $this->step = $step;
    }

    public function rewind()
    {
        $this->value = $this->initial;
    }

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

    public function next()
    {
        $this->value += $this->step;

        ++$this->key;
    }

    public function valid()
    {
        return $this->value <= $this->limit;
    }

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

And we can use it just like this:

foreach(new XRange(1, 10, 1) as $value) {

    echo $value;
}

Upshot:

12345678910

See working on IDEONE

Still thinking of a way to make the code simpler, we could do so:

function xrange($value, $limit, $step = 1)
{
     return new XRange($value, $limit, $step);
}


foreach(xrange(1, 10) as $key => $value) {

}

It is worth remembering that between the implementation of a Iterator would be faster than the function range, with a view to 1 to 10.000, range would be a "villain" in memory consumption; in the case of the class XRange we would have a value for each iteration.

  • Iterator would not be "faster", in fact it is probably slower as it includes all generation overhead and subsequent manipulation of the object. The correct is to say that Iterator would possibly be more space efficient (RAM), but only if you are not going to use all the generated sequences (e.g., if you break the iteration when $value == 5000).

  • @Brunorb I always confuse between the two terms. In fact, not always the fact of spending a lot of memory means that it is slower. It’s the same comparison we can make between using fetch and fetchAll

Browser other questions tagged

You are not signed in. Login or sign up in order to post.