что делает оператор yield php

Что означает yield в PHP?

Я недавно наткнулся на этот код:

Я никогда не видел этого yield ключевое слово раньше. Попытка запустить код, который я получаю

Parse error: синтаксическая ошибка, непредвиденная T_VARIABLE в строке x

что это yield сайта? Это даже действительный PHP? И если да, то как мне его использовать?

5 ответов

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

что такое функция генератора?

функция генератора является более компактным и эффективным способом записи итератор. Он позволяет определить функцию (your xrange ), что составит вычислить и возвратить значения пока вы петля над ним:

это создаст следующий вывод:

в чем разница с обычными функциями?

теперь вы можете задаться вопросом, почему мы не просто используем родной PHP range функции для достижения этого вывода. И ты прав. Результат будет таким же. Разница как мы туда попали.

при использовании range PHP, выполнит его, создаст весь массив чисел в памяти и return это весь массив до foreach цикл, который затем перейдет через него и выведет значения. Другими словами, foreach будет работать на сам массив. The и foreach только «поговорить» один раз. Думай об этом, как о посылке по почте. Курьер передаст вам посылку и уйдет. И потом развернуть весь пакет, вынимаю все, что там есть.

зачем мне это нужно?

очевидно, что генераторы могут использоваться для работы с ограничениями памяти. В зависимости от вашей среды, выполнение range(1, 1000000) будет фатальным ваш скрипт, тогда как то же самое с генератором будет просто работать нормально. Или, как говорит Википедия:

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

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

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

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

Источник

Как работает yield

На StackOverflow часто задают вопросы, подробно освещённые в документации. Ценность их в том, что на некоторые из них кто-нибудь даёт ответ, обладающий гораздо большей степенью ясности и наглядности, чем может себе позволить документация. Этот — один из них.

Вот исходный вопрос:

Как используется ключевое слово yield в Python? Что оно делает?

Например, я пытаюсь понять этот код (**):

Что происходит при вызове метода _get_child_candidates? Возвращается список, какой-то элемент? Вызывается ли он снова? Когда последующие вызовы прекращаются?

** Код принадлежит Jochen Schulz (jrschulz), который написал отличную Python-библиотеку для метрических пространств. Вот ссылка на исходники: http://well-adjusted.de/

Итераторы

Для понимания, что делает yield, необходимо понимать, что такое генераторы. Генераторам же предшествуют итераторы. Когда вы создаёте список, вы можете считывать его элементы один за другим — это называется итерацией:

Mylist является итерируемым объектом. Когда вы создаёте список, используя генераторное выражение, вы создаёте также итератор:

Всё, к чему можно применить конструкцию «for… in. », является итерируемым объектом: списки, строки, файлы… Это удобно, потому что можно считывать из них значения сколько потребуется — однако все значения хранятся в памяти, а это не всегда желательно, если у вас много значений.

Генераторы

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

Всё то же самое, разве что используются круглые скобки вместо квадратных. НО: нельзя применить конструкцию for i in mygenerator второй раз, так как генератор может быть использован только единожды: он вычисляет 0, потом забывает про него и вычисляет 1, завершаяя вычислением 4 — одно за другим.

Yield

Yield это ключевое слово, которое используется примерно как return — отличие в том, что функция вернёт генератор.

В данном случае пример бесполезный, но это удобно, если вы знаете, что функция вернёт большой набор значений, который надо будет прочитать только один раз.

Чтобы освоить yield, вы должны понимать, что когда вы вызываете функцию, код внутри тела функции не исполняется. Функция только возвращает объект-генератор — немного мудрёно 🙂

Ваш код будет вызываться каждый раз, когда for обращается к генератору.

Теперь трудная часть:

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

Генератор считается пустым, как только при исполнении кода функции не встречается yield. Это может случиться из-за конца цикла, или же если не выполняется какое-то из условий «if/else».

Объяснение кода из исходного вопроса

Читатель может остановиться здесь, или же прочитать ещё немного о продвинутом использовании генераторов:

Контроль за исчерпанием генератора

Это может оказаться полезным для разных целей вроде управления доступом к какому-нибудь ресурсу.

Ваш лучший друг Itertools

Модуль itertools содержит специальные функции для работы с итерируемыми объектами. Желаете продублировать генератор? Соединить два генератора последовательно? Сгруппировать значения вложенных списков в одну строчку? Применить map или zip без создания ещё одного списка?

Просто добавьте import itertools.

Хотите пример? Давайте посмотрим на возможные порядки финиширования на скачках (4 лошади):

Понимание внутреннего механизма итерации

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

Больше информации по данному вопросу доступно в статье про то, как работает цикл for.

Источник

Что означает yield в PHP?

Я недавно наткнулся на этот код:

Я никогда такого не видел yield ключевое слово раньше. Пытаюсь запустить код я получаю

ошибка синтаксического анализа: синтаксическая ошибка, неожиданный T_VARIABLE в строке x

что это yield ключевое слово? Это даже действительный PHP? И если да, то как мне его использовать?

5 ответов:

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

что такое функция генератора?

функция генератора эффективно является более компактным и эффективным способом записи итератор. Это позволяет определить функцию (ваш xrange ), что составит вычислить и возвратить значения пока вы петля над ним:

это создаст следующий вывод:

в чем разница с нормальными функциями?

теперь вы можете задаться вопросом, почему мы не просто используем родной PHP range функции для достижения этого вывода. И ты прав. Результат будет таким же. Разница как мы туда попали.

при использовании range PHP, выполнит его, создаст весь массив чисел в памяти и return это весь массив до foreach цикл, который затем пройдет по нему и выведет значения. Другими словами, foreach будет работать на сам массив. Элемент

yield ключевое слово служит для определения «генераторов» в PHP 5.5. Хорошо, тогда что такое генератор?

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

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

отсюда: генераторы = генераторы, Другие функции (просто простые функции) = функции.

Итак, они полезны когда:

вам нужно делать простые вещи (или простые вещи);

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

вам нужно генерировать большие объемы памяти для сохранения данных;

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

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

вам нужно улучшить производительность.

они могут работать быстрее, чем функции в некоторых случаях (см. предыдущий преимущество);

Источник

Что значит yield в PHP?

Я недавно наткнулся на этот код:

Я никогда не видел это yield ключевое слово раньше. Пытаясь запустить код, который я получаю

Ошибка разбора: синтаксическая ошибка, неожиданный T_VARIABLE в строке x

Так что же это за yield ключевое слово? Это даже действительный PHP? И если это так, как я могу его использовать?

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

Что такое функция генератора?

Это создаст следующий вывод:

В чем разница с обычными функциями?

Теперь вы можете задаться вопросом, почему мы не просто используем нативную range функцию PHP для достижения этого результата. И ты прав. Вывод будет таким же. Разница в том, как мы туда попали.

Зачем мне это нужно?

Очевидно, что генераторы можно использовать для обхода ограничений памяти. В зависимости от вашей среды выполнение range(1, 1000000) сценария приведет к фатальному сценарию, тогда как то же самое с генератором будет работать нормально. Или, как говорит Википедия:

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

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

Источник

Генераторы в действии

Небольшое вступление

Не так давно я решил для себя, что пора восполнить большой пробел в знаниях и решил прочитать про переходы между версиями PHP, т.к. понимал, что остался где-то между 5.2 и 5.3 и этот пробел необходимо как-то устранить. До этого я читал про namespaces, traits и т.д, но дальше чтения не уходило. И вот тут я заметил генераторы, почитал документацию, одну из статей на хабре на этот счет и после этого возникла мысль — а как раньше без них жили-то?

Данным переводом хочу помочь хотя бы новичкам, поскольку на php.net документация по генераторам на английском и, на мой взгляд, должным образом не раскрывает всю идею и места применения. Текста много, кода чуть меньше, картинок нет. Потребуются общие знания, например, про итераторы. Очевидный код комментировать не буду, а вот сложные для понимания примеры постараюсь объяснить в силу своих знаний.

UPD1: Изменил расплывчатую формулировку, про которую говорили в комментариях.
UPD2: Добавил решение с принудительным break.

Теория

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

Это обычное решение, ничего странного тут нет. Но что если нам нужно что-то более абстрактное? Скажем, генерировать строки из абстрактного источника. Да, сегодня это может быть файл, но завтра мы решим, что более удачным решением будет база данных или вообще что-то иное.

Сейчас у нас есть два пути решения данной задачи — мы можем вернуть массив или итератор. Но возвращая массив есть несколько проблем: во-первых мы не знаем сколько нам нужно памяти (вдруг файл у нас размером 30 гб?), а во-вторых, возможно, мы и вовсе не сможем описать наш источник как массив (например, мы можем возвращать бесконечные порции данных и попробуй угадай когда этот поток закончится, если ты клиент).

Итак, остаются итераторы. Наш пример очень просто описать через итератор. Тем более, что в PHP уже есть готовый класс для этого — SPLFileObject. Но давайте оставим его и напишем что-то свое.

Совсем просто, не так ли? Хорошо, не совсем, но уже что-то. Хотя если мы взглянем на пример внимательнее, то увидим, что мы не совсем точно описали итератор, поскольку двойной вызов метода current() не даст нам ожидаемый результат в виде одного и того же значения.
Я (автор статьи, не «переводчик») сделал это специально, чтобы показать, что замена процедуры на итератор не всегда является простой задачей, поскольку в реальных ситуациях все куда сложнее. Давайте сделаем правильный итератор для нашего файла.

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

Намного проще! Да, это почти как первый пример с функцией, только появилось исключение и ключевое слово yield.

Итак, как оно работает?

Очень важно понимать, что в примере выше изменяется возвращаемое значение функции. Это не null, как может показаться с первого взгляда. Наличие yield говорит о том, что PHP вернет нам специальный класс — генератор. Генератор ведет себя также, как и итератор, поскольку он реализует его. И использовать генератор можно аналогично итераторам.

Вся фишка здесь в том, что мы можем писать код как угодно и просто выбрасывать (yield, йелднуть, йелдануть… не знаю как перевести правильнее, когда есть бросание исключений) каждый раз новое значение когда нам это надо. Итак, как же оно работает? Когда мы вызываем функцию getLines(), PHP выполнит код до первой встречи ключевого слова yield, на котором он запомнит это значение и вернет генератор. Затем, будет вызов метода next() у генератора (который описан нами или итератором), PHP снова выполнит код, только начнет его не с самого начала, а начиная с прошлого значения, которое мы благополучно выбросили и забыли о нем, и опять, до следующего yield или же конца функции, или return. Зная этот алгоритм, теперь можно сделать полезный генератор:

Возможно, с первого взгляда не совсем понятно что это, да и вообще бесконечный цикл все испортит. Да, эта функция и будет работать как бесконечный цикл. Но посмотрите внимательнее — это ведь числа Фибоначчи.

Нужно отметить, что генераторы не являются заменой итераторам. Это лишь простой путь их получения. Итераторы по-прежнему являются мощным инструментом.

Сложный пример

Нам нужно сделать собственный ArrayObject. Вместо того, чтобы делать итератор, сделаем небольшой трюк с генератором. Интерфейс IteratorAggregate требует от нас всего один метод — getIterator(). Так как генератор возвращает объект, реализующий итератор, то мы можем переопределить этот метод таким образом, чтобы он возвращал генератор. Все просто:

В точку! Теперь мы можем перебрать все свойства нашего массива через генератор или через обычный синтаксис обращения по ключу.

Отправляем данные обратно

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

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

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

Нужно боольше примеров!

Вообще генераторы можно применять во многих задачах. Одна из них — симуляция потоков. Сначала мы определяем каждый поток как генератор. Затем выбрасываем сигнал управления родителю, чтобы тот смог передать сигнал для работы следующему потоку. Построим такую систему, которая работает с разными источниками данных (работаем с неблокирующим вводом-выводом). Вот пример такой системы:

Заключение

Генераторы — ОЧЕНЬ мощная штука. Они позволяют очень сильно упростить код. Подумайте только, вы можете написать функцию для диапазона чисел в одну строчку кода:

Коротко и просто. Легко читается, легко понять как работает и очень производительно — быстрее, чем с итератором.

Оригинал статьи — Anthony Ferrara @ blog.ircmaxell.com

В комментариях возник популярный вопрос о том, что делать, когда генератор (вернее сказать, его перебор через foreach) принудительно завершает свою работу, например, через break. В таком случае, если мы имеем дело с перебором файла, как из первого примера, то есть риск того, что никогда не сработает fclose, так как генератор попросту «забывает» о нем. Одно из самых верных решений предложил weirdan (#) — использовать конструкцию try <… >finally <… >, где в блоке finally очищаем открытые ресурсы. Данный блок сработает всегда при завершении перебора генератора, но есть маленький нюанс: если перебор генератора отработал до конца (без break) нормально, то выполнится и код после блока finally.

Кратко о генераторах

— Не добавляют нового функционала в язык
— Быстрее*
— Возобновление работы генератора происходит с последнего «выброса» yield
— В генератор можно отправлять значения и исключения (через метод throw())
— Генераторы однонаправлены, т.е. нельзя вернуться назад
— Меньше кода в большинстве случаев, более простые для понимания конструкции

* Основываясь на этих результатах.
При больших масштабах перебора — генераторы быстрее. Примерно в 4 раза быстрее чем итераторы и на 40% быстрее обычного перебора. При небольшом количестве элементов могут быть медленнее обычного перебора, но все еще быстрее итераторов.

Если сообщество одобрит перевод и посчитает его хорошим (а главное, не утверждающим чепуху и не меняющим суть кода), мне будет интересно иногда переводить другие статьи.
Думаю, не будет лишним перевести и собрать в кучку статьи, публикуемые сейчас на phpmaster про структуры данных.
Также буду рад любым замечаниям, наставлениям, комментариям про ошибки как в тексте с кодом, так и в самом переводе.

Источник

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *