Поиск…


Зачем использовать генератор?

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

Например, рассмотрим функцию ниже.

function randomNumbers(int $length)
{
    $array = [];
    
    for ($i = 0; $i < $length; $i++) {
        $array[] = mt_rand(1, 10);
    }
    
    return $array;
}

Вся эта функция создает массив, заполненный случайными числами. Чтобы использовать его, мы можем сделать randomNumbers(10) , что даст нам массив из 10 случайных чисел. Что делать, если мы хотим генерировать миллион случайных чисел? randomNumbers(1000000) сделают это для нас, но при стоимости памяти. Один миллион целых чисел, хранящихся в массиве, использует приблизительно 33 мегабайта памяти.

$startMemory = memory_get_usage();

$randomNumbers = randomNumbers(1000000);

echo memory_get_usage() - $startMemory, ' bytes';

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

Повторная запись randomNumbers () с использованием генератора

randomNumbers() может быть переписана для использования генератора.

<?php

function randomNumbers(int $length)
{
    for ($i = 0; $i < $length; $i++) {
        // yield tells the PHP interpreter that this value
        // should be the one used in the current iteration.
        yield mt_rand(1, 10);
    }
}

foreach (randomNumbers(10) as $number) {
    echo "$number\n";
}

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

Чтение большого файла с генератором

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

<?php

class CsvReader
{
    protected $file;
 
    public function __construct($filePath) {
        $this->file = fopen($filePath, 'r');
    }
 
    public function rows()
    {
        while (!feof($this->file)) {
            $row = fgetcsv($this->file, 4096);
            
            yield $row;
        }
        
        return;
    }
}
 
$csv = new CsvReader('/path/to/huge/csv/file.csv');

foreach ($csv->rows() as $row) {
    // Do something with the CSV row.
}

Ключевое слово доходности

Оператор yield похож на оператор return, за исключением того, что вместо прекращения выполнения функции и возврата, yield вместо этого возвращает объект Generator и приостанавливает выполнение функции генератора.

Вот пример функции диапазона, написанной как генератор:

function gen_one_to_three() {
    for ($i = 1; $i <= 3; $i++) {
        // Note that $i is preserved between yields.
        yield $i;
    }
}

Вы можете видеть, что эта функция возвращает объект Generator, var_dump вывод var_dump :

var_dump(gen_one_to_three())

# Outputs:
class Generator (0) {
}

Условные значения

Затем объект Generator можно повторить как массив.

foreach (gen_one_to_three() as $value) {
    echo "$value\n";
}

Вышеприведенный пример выводит:

1
2
3

Учет значений с помощью клавиш

В дополнение к уступающим значениям вы также можете получить пары ключ / значение.

function gen_one_to_three() {
    $keys = ["first", "second", "third"];

    for ($i = 1; $i <= 3; $i++) {
        // Note that $i is preserved between yields.
        yield $keys[$i - 1] => $i;
    }
}

foreach (gen_one_to_three() as $key => $value) {
    echo "$key: $value\n";
}

Вышеприведенный пример выводит:

first: 1
second: 2
third: 3

Использование функции send () для передачи значений генератору

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

//Imagining accessing a large amount of data from a server, here is the generator for this:
function generateDataFromServerDemo()
{
    $indexCurrentRun = 0; //In this example in place of data from the server, I just send feedback everytime a loop ran through.

    $timeout = false;
    while (!$timeout)
    {
        $timeout = yield $indexCurrentRun; // Values are passed to caller. The next time the generator is called, it will start at this statement. If send() is used, $timeout will take this value.
        $indexCurrentRun++;
    }

    yield 'X of bytes are missing. </br>';
}

// Start using the generator
$generatorDataFromServer = generateDataFromServerDemo ();
foreach($generatorDataFromServer as $numberOfRuns)
{
    if ($numberOfRuns < 10)
    {
        echo $numberOfRuns . "</br>";
    }
    else
    {
        $generatorDataFromServer->send(true); //sending data to the generator
        echo $generatorDataFromServer->current(); //accessing the latest element (hinting how many bytes are still missing.
    }
}

Результат:

введите описание изображения здесь



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow