Ответы пользователя по тегу PHP
  • Какая разница между генераторами и массивами?

    @Garry_Galler
    Почему генераторы потребляют меньше памяти, чем массивы?

    Простой ответ.

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

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

    Сложный ответ (для тех, кто умеет читать много букв)

    Генератор это подвид итератора. Итератор в контексте программирования это поведенческий паттерн, описанный в знаменитой книге "банды четырех". Его суть - в предоставлении "возможности последовательно обходить элементы составных объектов, не раскрывая их внутреннего представления."

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

    Генератор это частный случай итератора, который, как правило, создается функцией возвращающей объект итератора. Для упрощения создания генераторов языки программирования предоставляют программистам специальное ключевое слово yield, которое должно использоваться вместо return (либо совместно с ним). yield создает итератор под капотом, позволяя не реализовывать весь протокол Итератора вручную.

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

    Еще одно определение генераторов:
    Генератор — это объект, который сразу при создании не вычисляет значения всех своих элементов.
    Он хранит в памяти только последний вычисленный элемент, правило перехода к следующему и условие, при котором выполнение прерывается.
    Вычисление следующего значения происходит лишь при выполнении метода next(). Предыдущее значение при этом теряется.
    Этим генераторы отличаются от списков — те хранят в памяти все свои элементы, и удалить их можно только программно. Вычисления с помощью генераторов называются ленивыми, они экономят память


    Суть работы yield одинакова для всех ЯП.
    Вот описание принципа работы yield для языка C# ( вы же все знаете Джона Скита? - главного эксперта stackoverflow и автора известных книг), где это ключевое слово появилось довольно давно (в 2005-м, в Python -2001-м, а вообще - в 1975 году) и многие другие ЯП со временем переняли его.

    При наличии оператора yield return внутри метода компилятор строит на основе данного метода конечный автомат. Для реализации итератора конечный автомат обладает следующими свойствами:

    1. Он имеет некое начальное состояние
    2. При вызове MoveNext() выполняется код из метода GetEnumerator() по тех по, пока не будет достигнут оператор yield return;
    3. Когда используется свойство Current, он возвращает последнее выданное значение.
    4. Он должен знать, когда выдача значений завершена, чтобы метод MoveNext() мог возвратить false;



    Генераторы как подкласс итераторов имеют огромное значение для так называемых "ленивых вычислений".

    Они позволяют создавать (вычислять) значения на лету и тем самым экономить память, в отличие от "энергичных вычислений".
    Таким образом, на основе генераторов можно реализовать создание бесконечных последовательностей, чтение из бесконечного потока, чтение данных не вмещающихся в ОЗУ компьютера, ленивые вычисления, конвейерную обработку данных (можно создавать целые pipelines из генераторов), асинхронные корутины и т .д.

    P.S. Генераторы не имеют никакого отношения к циклам (цикл это только лишь один из способов обхода генераторных коллекций - извлечения значений из них ). Также генераторы не являются никаким синтаксическим сахаром ("красивой оберткой для цикла"), как было написано в других ответах. Синтаксических сахаром является исключительно само ключевое слово yield. При выполнении кода с yield он компилируется в Finite State Machine .
    Ответ написан
    6 комментариев