Pull to refresh

Comments 25

Yield вполне можно перевести как «передать» или «отдать». А в целом – весьма хороший перевод.
Спасибо!
Сам бы себе инвайт я бы не дал за этот пост, но раз кидают в избранное — значит это кому-нибудь нужно.
Сразу скажу главную вещь — генераторы никоим образом не добавляют новых возможностей языку.
Если так смотреть, то можно вообще программировать на Брейфаке, ничего нового добавить уже нельзя.
Ну автор статьи (не я) имел в виду то, что коренным образом ничего нового языку генераторы не добавили, а лишь упростили доступ к итераторам и их использование.
Здесь скорее моя ошибка в переводе. Возможность новая (так она и заявлена), но она двоякая из-за того, что это скорее дополнение к итераторам.
> Сразу скажу главную вещь — генераторы никоим образом не добавляют новых возможностей языку.

yield ставит на паузу выполнение функции, полностью сохраняя её состояние. Что это, как не новая возможность. В частности, именно на иелдах работает асинхронный питоновский фреймворк tornado.
да, чуть выше согласился, что это моя ошибка в переводе
php-скрипт рано или поздно завершается, и что происходит с этой паузой? Дальнейший код выполняется один раз? Не выполняется никогда?
если я правильно понял вопрос, то тогда вроде второй вариант — не выполняется никогда, поскольку последующий запуск генератора не будет знать о предыдущем запуске ничего. каждый новый запуск генератора выполняется с самого начала.
или вы имели в виду принудительную остановку генератора во время его работы?
поменял формулировку, теперь вроде как новые возможности действительно добавляют новые возможности)
Спасибо за статью. Планируем как раз скоро переход на 5.5.
Меня правда несколько коробит, когда вижу code-style, не соответствующий PSR-2. Почему люди не хотят приучаться к хорошему…
Наверное потому, что спор насчет положения {} никогда не закончится :) (впрочем расположение скобок () и отступов в декларациях функций там еще более спорное).
>Не добавляют нового функционала в язык
ну да, только это в php так) например в ruby .each, .with_index, .each_with_obj и прочее, одни из базовых концепций) ну т.е. итераторы/генераторы и прочее, что составляет базис. эх, может когда нибудь в php появятся фишки типа loop+generator как в ruby и пара фишек с closure, вроде как все необходимое для этого впилили. :)

p.s. сам похапе-программист, да :)

>йелдануть
lol =D
У меня вопрос тем, кто уже хорошо разобрался в генераторах. Пусть у нас есть первый генератор из этой статьи — getLines, который выдаёт строки из файла. Мы вставили его в цикл foreach, и внутри цикла при каком-то условии делаем break. Ну, предположим, нам надо дочитать только до определённой строки, а что дальше в файле, нам не интересно.

Вопрос: я правильно понимаю, что файл после этого останется незакрытым (fclose никогда не вызовется)? Если да, то как с этим бороться?
Неправильно, вызовется. break прерывает выполнение цикла точно так же как без генераторов. После цикла fclose никуда не денется. Вот если вы вставите return, тогда да. Но опять же, никаких различий со случаем без yield.
Хм, странно. Начал пробовать до вашего комментария, ответа, честно, не знал и решил затестить.
В голове возникло 2 решения обхода «возможной проблемы»:
1. Передать в генератор не имя файла, а сам fopen. Но могли быть проблемы во времени выполнения.
2. Бросить исключение и внутри catch закрыть файл.

Покажется странным, но когда делается break, fclose почему-то не срабатывает в таком случае.
В варианте передачи fopen нужно самому делать fclose, вне генератора.
C исключением, как мне показалось, самый толковый вариант — оборачиваем цикл в try, дальше либо в catch закрываем fopen, либо catch оставляем пустым и дальше генератор доходит до самого конца сам, тем самым закрывая fopen.

Все 3 варианта опробовал тут kocou.yTko.ws/gen_test.php || pastebin.com/zuqQd0LT

Поправьте, если где ошибаюсь где-то

Warning как раз из-за того, что сначала fclose сработал в catch, а затем в конце генератора.
Я не так понял david_mz и решил, что он говорит о цикле внутри генератора. Значит мой ответ не верен. В вашем примере с исключением нужно оставить пустой catch, тогда fclose будет вызываться один раз.
Файл остается незакрытым, да. После чтения RFC на генераторы придумался следующий вариант:
    function getLines($filename)
    {
        try {
            $fp = fopen($filename, 'r+');
            while (!feof($fp)) {
                yield fgets($fp);
            }
        } finally {
            var_dump('gonna close');
            fclose($fp);
        }
    }

Основанный на вот этой фразе: «If the generator contains (relevant) finally blocks those will be run»
Тоже интересное решение, в таком случае (принудительный break) выполняется только блок finally и не выполняет остальной код генератора.
Если же просто заканчивается перебор то выполняется блок finally и остальной код генератора. Чуть позже попробую добавить это в статью.
Вау! Это прекрасное решение. Удивительно, что в основной доке про это ни слова, и надо лезть в RFC.

Мне кажется (это уже к yTko), этот паттерн — освобождение ресурсов и памяти строго в finally — должен обязательно упоминаться в любой статье про генераторы. Потому что генераторы в foraech — обычное дело, а про то, что юзер в любой момент может сказать break, легко забыть.
Добавьте перевод через edit.php.net, там его просмотрят и сапрувят. Думаю что многим эта статья будет полезна! Спасибо!
Сломал мозг на примере с функцией fetchBytesFromFile
там два yield, один из них принимает значение, второй нет

В строчке ниже мы всегда передаем значение
while ($buffer .= $byteGenerator->send($bytesNeeded)) { # всегда считываем порцию разного размера


Правильно ли я понимаю, что нет смысла все время передавать туда значение для $length, ведь оно сетается лишь в начале и потом уже не меняется?
В цикле же меняется:
        $length = yield fread($f, $length);       # выбрасываем блок данных
Sign up to leave a comment.

Articles