Генераторы в PHP

В этой статье мы будем изучать генераторы, которые обеспечивают простой способ реализовать простые итераторы без накладных расходов и сложности интерфейса Итератора.

Как работают генераторы?

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

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

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

<?php
function nums() {
    echo "Генератор начался"; 
    for ($i = 0; $i < 5; ++$i) {
        yield $i;
        echo "Приводится $i";
    }
    echo "Генератор закончился"; 
}
 
foreach (nums() as $v);
?>

Вывод вышеуказонного кода будет:

Генератор начался

Приводится 0

Приводится 1

Приводится 2

Приводится 3

Приводится 4

Генератор закончился

Наш первый генератор

Генераторы не новая концепция, и уже существуют в таких языках, как C#, Python, JavaScript, и Ruby, а, как правило, идентифицируются по их использованию yield ключевого слова. Ниже приведен пример в Python:

def file_lines(filename):
    file = open(filename)
    for line in file:
        yield line
    file.close()
 
for line in file_lines('somefile'):
	.............

Давайте перезапишем генератор Python-а в PHP. (Отметим, что оба фрагмента не выполняют никакого вида ошибочной проверки.)

<?php
function file_lines($filename) {
    $file = fopen($filename, 'r'); 
    while (($line = fgets($file)) !== false) {
        yield $line; 
    } 
    fclose($file); 
}
 
foreach (file_lines('somefile') as $line) {
    ...............
}
?>

Функция генератора открывает файл а затем считает каждую строку файла, когда это требуется. Каждый раз, когда генератор вызывается, она продолжает от того места, откуда она была прервана. Она не начинается с самого начала, так как его состояние было сохранено, когда подтверждение действия выполнилось. После того как все линии были прочитаны, генератор просто отключается, и цикл заканчивается.

Возвращение ключей

PHP iterators состоят из пар ключевого/значения. В нашем примере, только значение было возвращаемым и поэтому ключи были числовыми (числовые ключи бывают по умолчанию). Если вы желаете вернуть ассоциативную пару, просто измените yield для включения ключа, используя синтаксис массива.

<?php
function file_lines($filename) {
    ...
        yield $key => $line; 
    ...
}
 
foreach (file_lines('somefile') as $key => $line) {
    .............
}
?>

Введение значения

yield не только возвращать значения, он также может получать значения из вне. Это делается путем вызова метода send() генератора объект со значением, если вы хотите передать и значение. Затем это значение может использоваться в вычислениях или делать другие вещи.

<?php
function nums() {
    for ($i = 0; $i < 5; ++$i) {
        // get a value from the caller
        $cmd = (yield $i);
        if ($cmd == 'stop') {
            return; // exit the generator
        }
    }
}
 
$gen = nums();
 
foreach ($gen as $v) {
    // we are satisfied
    if ($v == 3) {
        $gen->send('stop');
    }
    echo "{$v}n";
}
?>

Вывод будет следующим:

0

1

2

3

Сохранение памяти с помощью генераторов

Генераторы удобны, расчета больших наборов, и если вы в то же время не хотите выделить память для всех результатов, или когда вы не знаете, вам нужны будут все результаты. Из-за способа обработки результатов, объем памяти может быть сведена к минимуму путем выделения памяти только для текущего результата.

Представьте себе функцию file(), которая возвращает все строки файла в виде массива. Управляя простой точкой отсчета(масштабом) для file() и наш демонстрационный пример функции file_lines(), каждый использующий те же самые случайные 100 текстовых файлов параграфа, используя Lipsum, показали, что функция file() использовала до 110 раз больше памяти, чем генератор.

<?php
// Тест 1
$m = memory_get_peak_usage();
foreach (file_lines('lipsum.txt') as $l);
echo memory_get_peak_usage() - $m, "n"; //Выводит 7336
 
// Тест 2
$m = memory_get_peak_usage();
foreach (file('lipsum.txt') as $l);
echo memory_get_peak_usage() - $m, "n"; // Выводит 148112
?>

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

Добавить комментарии



[CODE] [/CODE]


Img

Добавил: techblogger

Добавлен: 2016-08-03 19:41:45

Сколько ошибок... Похоже, местами использовался машинный перевод.