PHP: Знакомство с генераторами - Manual CakeFest 2017 NYC, the Official CakePHP Conference


    Синтаксис генераторов »
    « Генераторы

    Edit Report a Bug

    Знакомство с генераторами

    (PHP 5 >= 5.5.0, PHP 7)

    Генераторы предоставляют лёгкий способ реализации простых Итераторов без использования дополнительных ресурсов или сложностей, связанных с реализацией класса, реализующего интерфейс Iterator.

    Генератор позволяет Вам писать код, использующий foreach для перебора набора данных без необходимости создания массива в памяти, что может привести к превышению вами лимита памяти, либо потребует довольно много времени для его создания. Вместо этого, Вы можете написать функцию-генератор, которая, по сути, является обычной функцией, за исключением того, что вместо возвращения единственного значения, генератор может yield столько раз, сколько необходимо для генерации значений, позволяющих перебрать исходный набор данных.

    Наглядным примером вышесказанного может послужить использование функции range() как генератора. Стандартная функция range() должна генерировать массив, состоящий из значений, и возвращать его, что может послужить результатом генерации огромных массивов: например, вызов range(0, 1000000), приведёт к использованию более чем 100 МБ памяти.

    В качестве альтернативы мы можем создать генератор xrange(), который использует память только для создания объекта Iterator и сохранения текущего состояния, что потребует не больше 1 килобайта памяти.

    Пример #1 Реализация range() как генератора

    function xrange($start, $limit, $step = 1) {
        if ($start < $limit) {
            if ($step <= 0) {
                throw new LogicException('Step must be +ve');
            }
            for ($i = $start; $i <= $limit; $i += $step) {
                yield $i;
            }
        } else {
            if ($step >= 0) {
                throw new LogicException('Step must be -ve');
            }
            for ($i = $start; $i >= $limit; $i += $step) {
                yield $i;
            }
        }
    }
    /* Обратите внимание, что и range() и xrange() дадут один и то-то вывод */
    echo 'Нечетные, однознаковые числа с помощью range():  ';
    foreach (range(1, 9, 2) as $number) {
        echo "$number ";
    }
    echo "\n";
    echo 'Нечетные, однознаковые числа с помощью xrange(): ';
    foreach (xrange(1, 9, 2) as $number) {
        echo "$number ";
    }

    Результат выполнения данного примера:


    Нечетные, однознаковые числа с помощью range(): 1 3 5 7 9 
    Нечетные, однознаковые числа с помощью xrange(): 1 3 5 7 9

    Объект Generator

    Когда функция генератор будет вызвана первый раз, она вернет объект встроенного класса Generator. Этот объект реализует интерфейс Iterator, станет однонаправленным объектом итератора и предоставит методы, с помощью которых можно управлять его состоянием, включая передачу в него и возвращения из него значений.


        function generator()
        {
            $complete = false;
            try {
                while (($result = some_function())) {
                    yield $result;
                }
                $complete = true;
            } finally {
                if (!$complete) {
                    // cleanup when loop breaks 
                } else {
                    // cleanup when loop completes
                }
            }
            // Do something only after loop completes
        }


    18
    dc at libertyskull dot com3 years ago
    $start_time=microtime(true);
    $array = array();
    $result = '';
    for($count=1000000; $count--;)
    {
      $array[]=$count/2;
    }
    foreach($array as $val)
    {
      $val += 145.56;
      $result .= $val;
    }
    $end_time=microtime(true);
    echo "time: ", bcsub($end_time, $start_time, 4), "\n";
    echo "memory (byte): ", memory_get_peak_usage(true), "\n";


    5
    montoriusz at gmail dot com9 months ago
    $some_state = 'initial';
    function gen() {
        global $some_state; 
        echo "gen() execution start\n";
        $some_state = "changed";
        yield 1;
        yield 2;
    }
    function peek_state() {
        global $some_state;
        echo "\$some_state = $some_state\n";
    }
    echo "calling gen()...\n";
    $result = gen();
    echo "gen() was called\n";
    peek_state();
    echo "iterating...\n";
    foreach ($result as $val) {
        echo "iteration: $val\n";
        peek_state();
    }


    -19
    damirchilo at outlook dot com1 year ago
    function gen_n($n){
        function gen_t($len){
            for($i = 1; $i < $len; $i++)
                yield $i; 
        }
        
        foreach(gen_t($n) as $out)
            printf("%d, ", $out);
        printf("%d", ++$out);
    }
    gen_n(15);




    Смотрите также:
    Описание на ru2.php.net
    Описание на php.ru