Вам бонус- начислено 1 монета за дневную активность. Сейчас у вас 1 монета

Назначение, синтаксис и примеры генераторов в php ( yield) кратко

Лекция



Привет, Вы узнаете о том , что такое yield, Разберем основные их виды и особенности использования. Еще будет много подробных примеров и описаний. Для того чтобы лучше понимать что такое yield, генератор , настоятельно рекомендую прочитать все из категории Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки (Frontend).

Назначение и синтаксис генератор а в php

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

Можно провести некую аналогию с "курсором" в базах данных и местом обработки данных в позиции курсора.

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

Когда вызывается генератор, он возвращает объект, который можно итерировать. Когда вы итерируете этот объект (например, в цикле foreach), PHP вызывает методы итерации объекта каждый раз, когда вам нужно новое значение, после чего сохраняет состояние генератора и при следующем вызове возвращает следующее значение.

Назначение, синтаксис  и примеры  генераторов в php ( yield)

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

1 2 3 

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

Вся суть генератора заключается в ключевом слове yield. В самом простом варианте оператор "yield" можно рассматривать как оператор "return", за исключением того, что вместо прекращения работы функции, "yield" только приостанавливает ее выполнение и возвращает текущее значение, и при следующем вызове функции она возобновит выполнения с места, на котором прервалась.

Делегирование генератора с помощью yield from

В PHP 7, делегирование генератора позволяет вам получать значения из другого генератора, объекта Traversable, или массива, используя yield from. Внешний генератор будет возвращать значения из внутреннего генератора, объекта или массива, до того момента, пока они их отдают, после чего продолжится выполнения внешнего генератора.

Если генератор используется с yield from, то выражение yield from также будет возвращать значения из внутреннего генератора.

Назначение, синтаксис  и примеры  генераторов в php ( yield)

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

1 2 3 4 5 6 7 8 9 10 

Генераторы как прерываемые функции

Чтобы перейти от генераторов к coroutines, важно понимать, как они работают внутри:

генераторы - это прерываемые функции, в которых yield операторы образуют точки прерывания.

Придерживаясь приведенного выше примера, если вы не вызываете xrange(1, 1000000)код в xrange()функции, фактически выполняется.

Вместо этого PHP просто возвращает экземпляр Generator класса, реализующего Iterator интерфейс:

$range = xrange(1, 1000000);
var_dump($range); // object(Generator)#1
var_dump($range instanceof Iterator); // bool(true)

Код запускается только после того, как вы вызовете один из методов итератора для объекта. Например, если вы вызываете $range->rewind() код, xrange() функция будет выполняться до первого появления yieldв потоке управления. В данном случае это означает, что $i = $startа затем yield $iзапускаются. Все, что было передано в yield оператор, затем можно получить с помощью $range->current().

Чтобы продолжить выполнение кода в генераторе, вам необходимо вызвать $range->next() метод. Это снова возобновит работу генератора до тех пор, пока не будет достигнут оператор yield. Таким образом, используя последовательность ->next() и ->current() вызовов, вы можете получить все значения от генератора, пока в какой - то момент не yield не попал больше. Ибо xrange()это случается, когда-то $iпревышает $end. В этом случае поток управления достигнет конца функции, и больше не останется кода для выполнения. Как только это произойдет, ->valid() метод вернет false, и итерация завершится.

Главное, что coroutines добавляют к вышеуказанной функциональности, - это возможность отправлять значения обратно в генератор. Об этом говорит сайт https://intellect.icu . Это превращает одностороннюю связь от генератора к вызывающему абоненту в двусторонний канал между ними.

Значения передаются в сопрограмму путем вызова ее ->send()метода вместо ->next()

Применение итераторов

Итерация по большому набору данных

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

Без использования итераторов это будет так:



private function getGoodsByStore($storeId)
{
    $goods = Goods::where('store_is',storeId)->get();
    $filteredGoods = [];

    foreach ($goods as $product) { 
            $filteredGoods[] = $this->makeEntity($product); 
    }

    return $filteredGoods;
}

проблему легко увидеть: чем больше товаров, тем больше нужно памяти для $filteredGoods.

Одно из решений - создать итератор, который бы итерировал $goods и возвращал что то без использования промежуточного массива $filteredGoods. Однако это не быстро сделатьс точки зрения написания кода.

Как альтернатива - есть возможность в php использовать генераторы вместо интератора.


private function getGoodsByStore($storeId)
{
    $goods = Goods::where('store_is',storeId)->get();

    foreach ($goods as $product) { 
            yield $this->makeEntity($product); 
    }

}

Рефакторинг метода getGoodsByStore для использования генератора очень прост: заменяем передачу значений в переменную  $filteredGoods и return конструкцией yield.

Т.к. массива $filteredGoods нет а есть итератор, или генератор, потребление памяти теперь будет постоянным, не важно, сколько товаров нужно вернуть, и мы уверены, что товары будут выбираться только по надобности.

Агрегация нескольких источников данных

Теперь рассмотрим момент получения $goods. Пердоложим что их получить можно с разных источников: реляционной БД и Elasticsearch.

Мы можем написать простой метод, агрегирующий эти два источники для начала без использвания генераторов:




private function getGoods()
{

$products = [];


    //  from DB  

    $goods = Goods::where('store_is',storeId)->get();

    foreach ($goods as $product) {
        $products[] =  $this->makeEntity($product);
    }


    //  from Elasticsearch  
    $cursor = $this->esClient->findAll();

    foreach ($cursor as $data) {
        $products[] =  = $this->makeEntity($data);
    }

    return $products;
}

Обратим внимание, количество потребляемой памяти при использовании такого способа очень зависит от числа товаров, получаемых из базы данных и Elasticsearch.

Тут можно использовать генераторы и возвратить результат:



private function getGoods()
{

    //  from DB  

    $goods = Goods::where('store_is',storeId)->get()->toArray();

    foreach ($goods as $product) {
        yield $this->makeEntity($product);
    }

    //  from Elasticsearch  
    $cursor = $this->esClient->findAll();

    foreach ($cursor as $data) {

        yield $this->makeEntity($data);

    } 
}

Так, конечно, лучше, но у нас есть другая проблема: метод getGoods выполняет несколькко отвественностей!

Можно выделить две ответственности (получение данных из БД и вызов Elasticsearch) в два метода:


private function getGoods()
{
    yield from $this->getGoodsFromDB();
    yield from $this->getGoodsFromES();
}

private function getGoodsFromDB()
{
    $goods = Goods::where('store_is',storeId)->get()->toArray();

    foreach ($goods as $product) {
        yield $this->makeEntity($product);
    }
}

private function getGoodsFromES()
{
    $cursor = $this->esClient->findAll();
    foreach ($cursor as $data) {

       yield $this->makeEntity($data);

    }
 }

Обратите внимание, использование yield from оператора (доступен с php 7.0), который позволяет делегировать использование генераторов.

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

yield from оператор работает с любым Traversable объектом, так что массивы и итераторы также могут быть использованы с этим оператором.

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


private function getGoods()
{
    yield new Product(…);
    yield from [new Product(…), new Product(…)];
    yield from new ArrayIterator([new Product(…), new Product(…)]);
    yield from $this->getEbooksFromFile();
    yield from $this->getEbooksFromDB();
}

Имитация асинхронных задач

Последнее, но тем не менее важное: генераторы так же могут быть использованы для имитации асинхронных задач. Есть возможность получить преимущества из-за того, что они могут быть прерваны и отправлять/принимать данные для реализации сопрограмм и даже многозадачности.

Отправление данных обратно генератору


Генераторы имеют возможности отправлять(принимать) себе данные, используя метод send().

Это может быть очень удобно, например, когда нужно сделать какой-то лог-файл.

Вместо того, чтобы писать целый класс для него, можно просто использовать генераторами:

function Loging($file) {
    $f = fopen($file, 'a');
    while (true) {          #  бесконечный цикл;
        $line = yield;      # бесконечно "слушаем" метод send() для установки нового значения $line;
        fwrite($f, $line);
    }
}
$log = Loging($file);
$log->send("event 1");
$log->send("event 2"); 

Читая приведенный выше logger()пример, вы подумали: «Зачем мне использовать для этого сопрограмму? Почему я не могу использовать обычный класс? », Тогда вы были совершенно правы. Пример демонстрирует базовое использование, но на самом деле нет никаких преимуществ от использования сопрограммы в этом контексте. Так обстоит дело с множеством примеров сопрограмм. Как уже упоминалось во введении, сопрограммы - это очень мощная концепция, но их приложения редки и часто достаточно сложны, что затрудняет создание простых и не надуманных примеров.

Таким образом

  • Генераторы используются как упрощенные итераторы;
  • Генераторы могут возвращать большие объемы данных без задействования дополнительной памяти;
  • Генераторы могут быть использованы для имитации многозадачности;
  • Генераторы можно объедененять с помощью делегирования генераторов;

В любом случае, я думаю, что это интересная тема, и я надеюсь, что вам она тоже показалась интересной. Комментарии приветствуются :)

Исследование, описанное в статье про yield, подчеркивает ее значимость в современном мире. Надеюсь, что теперь ты понял что такое yield, генератор и для чего все это нужно, а если не понял, или есть замечания, то не стесняйся, пиши или спрашивай в комментариях, с удовольствием отвечу. Для того чтобы глубже понять настоятельно рекомендую изучить всю информацию из категории Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки (Frontend)

создано: 2020-08-28
обновлено: 2023-09-22
132265



Рейтиг 9 of 10. count vote: 2
Вы довольны ?:


Поделиться:

Найди готовое или заработай

С нашими удобными сервисами без комиссии*

Как это работает? | Узнать цену?

Найти исполнителя
$0 / весь год.
  • У вас есть задание, но нет времени его делать
  • Вы хотите найти профессионала для выплнения задания
  • Возможно примерение функции гаранта на сделку
  • Приорететная поддержка
  • идеально подходит для студентов, у которых нет времени для решения заданий
Готовое решение
$0 / весь год.
  • Вы можите продать(исполнителем) или купить(заказчиком) готовое решение
  • Вам предоставят готовое решение
  • Будет предоставлено в минимальные сроки т.к. задание уже готовое
  • Вы получите базовую гарантию 8 дней
  • Вы можете заработать на материалах
  • подходит как для студентов так и для преподавателей
Я исполнитель
$0 / весь год.
  • Вы профессионал своего дела
  • У вас есть опыт и желание зарабатывать
  • Вы хотите помочь в решении задач или написании работ
  • Возможно примерение функции гаранта на сделку
  • подходит для опытных студентов так и для преподавателей



Комментарии


Оставить комментарий
Если у вас есть какое-либо предложение, идея, благодарность или комментарий, не стесняйтесь писать. Мы очень ценим отзывы и рады услышать ваше мнение.
To reply

Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки (Frontend)

Термины: Выполнение скриптов на стороне клиента JavaScript, jqvery, JS фреймворки (Frontend)