За последние 24 часа нас посетили 20383 программиста и 1090 роботов. Сейчас ищут 805 программистов ...

Генераторы (yield)

Тема в разделе "Прочие вопросы по PHP", создана пользователем [vs], 11 июн 2015.

  1. [vs]

    [vs] Суперстар
    Команда форума Модератор

    С нами с:
    27 сен 2007
    Сообщения:
    10.553
    Симпатии:
    631
    Сабж

    Кто юзал? Какие плюсы/минусы?
     
  2. romach

    romach Старожил

    С нами с:
    26 окт 2013
    Сообщения:
    2.904
    Симпатии:
    719
    Присоединяюсь к вопросу. Вроде как все круто, а вроде и непонятно где это заюзать )
     
  3. Fell-x27

    Fell-x27 Суперстар
    Команда форума Модератор

    С нами с:
    25 июл 2013
    Сообщения:
    12.155
    Симпатии:
    1.769
    Адрес:
    :сердА
    Чем отличается от сбора данных в результирующий массив с последующей его отдачей, кроме неочевидности происходящего, если не знать, что это за хрень, и, потенциально, усложнения отладки и багханта?
     
  4. MiksIr

    MiksIr Активный пользователь

    С нами с:
    29 ноя 2006
    Сообщения:
    2.340
    Симпатии:
    44
    Тем, что данных может быть очень много из внешнего источника. Или может быть бесконечный ряд.
    Подготовка результатирующего массива тут бездумная трата ресурсов... их банально просто может не хватить.
     
  5. Zuldek

    Zuldek Старожил

    С нами с:
    13 май 2014
    Сообщения:
    2.381
    Симпатии:
    344
    Адрес:
    Лондон, Тисовая улица, дом 4, чулан под лестницей
    соглашусь что код не столь очевиден, хотя ide у меня видит определение.
    Не юзаю, но в принципе ничего против не имею. Скажем так, если проект новый, почему бы и нет. В новых и трейты и иные веянения можно юзать. А вот в старых не вижу смысла внедрять: банально усложнит работу с кодом старым и лишние нейроны в голове лопнут).
     
  6. Fell-x27

    Fell-x27 Суперстар
    Команда форума Модератор

    С нами с:
    25 июл 2013
    Сообщения:
    12.155
    Симпатии:
    1.769
    Адрес:
    :сердА
    Да, но в доках посмотри использование. Генератор не предоставляет поток. Он возвращает массив... То есть ты сначала пускаешь генератор, он наполняет переменную, потом ты юзаешь ее. Расход ресурсов такой же как и при использовании результирующего массива. Ну, разве что в момент возвращения значения нет фазы копирования, которая на несколько тактов процессора удваивает занимаемое количество ОП, после чего оно опять приходит в норму.

    Но, чтобы уронить что-то, пых должен за раз провернуть если не гигабайты, то сотни метров данных. Учитывая то, что это таки язык, заточенный именно под веб, сложно представить подобную ситуацию. Я с таким встречался только в тестовых кодах, либо "бенчмарках", когда сравниваешь пару-тройку алгоритмов на большой "дистанции". Но это же ресерч, а не повседневная нужда.
     
  7. MiksIr

    MiksIr Активный пользователь

    С нами с:
    29 ноя 2006
    Сообщения:
    2.340
    Симпатии:
    44
    Нет, вы не поняли как он работает.

    Генератор - это форма итератора. В плюсах - более краткая и понятная запись. В минусах - движение только вперед.

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

    Код (Text):
    1. function gen_one_to_three() {
    2.     for ($i = 1; $i <= 3; $i++) {
    3.         // Note that $i is preserved between yields.
    4.         yield $i;
    5.     }
    6. }
    7.  
    8. $generator = gen_one_to_three();
    В $generator сейчас будет не [1, 2, 3], а ссылка на генератор, который можно использовать как итератор.

    По сути любой генератор можно переделать как итератор (класс Iterator). Просто генератор - более компактная запись.

    Добавлено спустя 1 минуту 29 секунд:
    http://php.net/manual/ru/language.generators.comparison.html
    Вот тут пример есть. Генератор и аналогичный ему итератор.
     
  8. Fell-x27

    Fell-x27 Суперстар
    Команда форума Модератор

    С нами с:
    25 июл 2013
    Сообщения:
    12.155
    Симпатии:
    1.769
    Адрес:
    :сердА
    Понятно, спасибо. Забавно, да.
     
  9. MiksIr

    MiksIr Активный пользователь

    С нами с:
    29 ноя 2006
    Сообщения:
    2.340
    Симпатии:
    44
    PHP уже давно вышел и стадии "заточенный под веб" и вполне используется и для других задач. Да и кто, как не вы фанат экономии каждого байта. Другое дело, что реальных кейсов действительно не так много. Но они есть.
    Кроме того, генераторы - удобный способ написания кода демона взаимодействующего с асинхронными сервисами.
     
  10. [vs]

    [vs] Суперстар
    Команда форума Модератор

    С нами с:
    27 сен 2007
    Сообщения:
    10.553
    Симпатии:
    631
    Пара практичных примеров. Ведение лога:
    Код (PHP):
    1. function log_writer($fname) {
    2.   $file = new SplFileObject($fname, 'a');
    3.   while (true) {
    4.     $line = yield;
    5.     $file->fwrite($line . PHP_EOL);
    6.   }
    7. }
    8.  
    9. $log = log_writer('log.txt');
    10. $log->send('Start');
    11. $log->send('Finish');
    оператор yield обеспечивает обратную связь.

    Возврат строки mysql из своего класса без необходимости предварительной агрегации в массив. Экономит память:
    Код (PHP):
    1.   public function selectRows($sql) {
    2.     $result = $this->query($sql);
    3.     while ($row = $result->fetch_assoc()) {
    4.       yield $row;
    5.     }
    6.   }
    7.   
    8.  
    9. foreach ($db->selectRows($sql) as $row) {
    10.   
    11. }
    12.  
    плюс перед отдачей массива в том, что строки не фетчатся, пока не потребуются. Плюс перед отдачей объекта mysqli_result в большей гибкости итератора, по сравнению с результом. А если нужен именно массив сразу со всеми строками, можно сделать так:
    Код (PHP):
    1. $array = iterator_to_array($db->selectRows($sql)); 
     
  11. Zuldek

    Zuldek Старожил

    С нами с:
    13 май 2014
    Сообщения:
    2.381
    Симпатии:
    344
    Адрес:
    Лондон, Тисовая улица, дом 4, чулан под лестницей
    В php7 также вводится конструкция yield from.
    То есть можно проделывать такую штуку:
    Код (PHP):
    1. function gen2($i) {
    2.   yield $i;
    3.   yield $i*2;
    4. }
    5.  
    6. function gen1() {
    7.   yield from [1,2];
    8.   yield from gen2(4);
    9. }
    10.  
    11. foreach (gen1() as $i) {
    12.   var_dump($i);
    13. } 
     
  12. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.625
    Симпатии:
    158
    этот код должен вывести: 1,2,4,8 ?
     
  13. Zuldek

    Zuldek Старожил

    С нами с:
    13 май 2014
    Сообщения:
    2.381
    Симпатии:
    344
    Адрес:
    Лондон, Тисовая улица, дом 4, чулан под лестницей