У меня есть пример кода Python, который мне нужно воспроизвести на C ++. Мне не требуется какое-либо конкретное решение (например, решения yield на основе совместной подпрограммы, хотя они также могут быть приемлемыми ответами), мне просто нужно каким-то образом воспроизвести семантику.
питон
Это основной генератор последовательности, явно слишком большой для хранения материализованной версии.
def pair_sequence():
for i in range(2**32):
for j in range(2**32):
yield (i, j)
Цель состоит в том, чтобы поддерживать два экземпляра указанной выше последовательности и выполнять итерацию по ним полусадочно, но по частям. В приведенном ниже примере first_pass
используется последовательность пар для инициализации буфера, а second_pass
регенерируется такая же точная последовательность и снова обрабатывается буфер.
def run():
seq1 = pair_sequence()
seq2 = pair_sequence()
buffer = [0] * 1000
first_pass(seq1, buffer)
second_pass(seq2, buffer)
... repeat ...
C ++
Единственное, что я могу найти для решения на C ++, - это имитировать yield
сопрограммы C ++, но я не нашел хороших справочников о том, как это сделать. Меня также интересуют альтернативные (не общие) решения этой проблемы. У меня недостаточно памяти для хранения копии последовательности между проходами.
Ответы:
Генераторы существуют в C ++ под другим именем: Итераторы ввода . Например, чтение из
std::cin
аналогично генераторуchar
.Вам просто нужно понять, что делает генератор:
В вашем тривиальном примере это достаточно просто. Концептуально:
Конечно, мы оборачиваем это как правильный класс:
Так что хм, да ... может быть, C ++ немного более подробный :)
источник
В C ++ есть итераторы, но реализовать итератор непросто: нужно проконсультироваться с концепциями итератора и тщательно разработать новый класс итератора для их реализации. К счастью, у Boost есть iterator_facade шаблон который должен помочь в реализации итераторов и генераторов, совместимых с итераторами.
Иногда для реализации итератора можно использовать бесстековую сопрограмму. .
PS См. Также эту статью, в которой упоминаются
switch
взлом Кристофера М. Кольхоффа и Boost.Coroutine Оливера Ковалька. Работа Оливера Kowalke в это период наблюдения на Boost.Coroutine Джованни П. Deretta.PS Думаю, можно еще написать своего рода генератор с лямбдами :
Или с функтором:
PS Вот генератор, реализованный с сопрограммами Mordor :
источник
Поскольку Boost.Coroutine2 теперь очень хорошо поддерживает его (я нашел его, потому что хотел решить ту же
yield
проблему), я публикую код C ++, который соответствует вашему первоначальному намерению:В этом примере
pair_sequence
не требуется дополнительных аргументов. Если это необходимо,std::bind
или лямбда должна использоваться для генерации объекта функции, который принимает только один аргумент (изpush_type
), когда он передается вcoro_t::pull_type
конструктор.источник
Все ответы, связанные с написанием собственного итератора, совершенно неверны. В таких ответах полностью упускается суть генераторов Python (одна из величайших и уникальных особенностей языка). Самое важное в генераторах - это то, что выполнение продолжается с того места, где оно было остановлено. С итераторами этого не происходит. Вместо этого вы должны вручную сохранить информацию о состоянии, чтобы при новом вызове operator ++ или operator * правильная информация находилась в самом начале следующего вызова функции. Вот почему написание собственного итератора C ++ - огромная боль; тогда как генераторы элегантны, их легко читать + писать.
Я не думаю, что есть хороший аналог генераторов Python в родном C ++, по крайней мере, пока (есть слухи, что yield появится в C ++ 17 ). Вы можете получить что-то похожее, прибегнув к помощи сторонних разработчиков (например, предложение Yongwei Boost) или применив свое собственное.
Я бы сказал, что самое близкое в родном C ++ - это потоки. Поток может поддерживать приостановленный набор локальных переменных и может продолжать выполнение с того места, где он был остановлен, очень похоже на генераторы, но вам нужно развернуть немного дополнительной инфраструктуры для поддержки связи между объектом-генератором и его вызывающей стороной. Например
Однако у этого решения есть несколько недостатков:
источник
Вероятно, вам следует проверить генераторы в std :: experimental в Visual Studio 2015, например: https://blogs.msdn.microsoft.com/vcblog/2014/11/12/resumable-functions-in-c/
Я думаю, это именно то, что вы ищете. Общие генераторы должны быть доступны в C ++ 17, поскольку это только экспериментальная функция Microsoft VC.
источник
Если вам нужно сделать это только для относительно небольшого числа конкретных генераторов, вы можете реализовать каждый как класс, где данные членов эквивалентны локальным переменным функции генератора Python. Затем у вас есть следующая функция, которая возвращает следующее, что выдаст генератор, обновляя при этом внутреннее состояние.
Я считаю, что это в основном похоже на то, как реализованы генераторы Python. Основное отличие состоит в том, что они могут запоминать смещение в байт-коде для функции генератора как часть «внутреннего состояния», что означает, что генераторы могут быть записаны в виде циклов, содержащих выходы. Вместо этого вам нужно будет вычислить следующее значение из предыдущего. В случае вашего
pair_sequence
это довольно тривиально. Может не быть для сложных генераторов.Вам также нужен способ обозначить прекращение. Если то, что вы возвращаете, «похоже на указатель» и NULL не должно быть допустимым выходным значением, вы можете использовать указатель NULL в качестве индикатора завершения. В противном случае вам понадобится внеполосный сигнал.
источник
Примерно так очень похоже:
Использование operator () - это только вопрос того, что вы хотите делать с этим генератором, вы также можете создать его как поток и, например, убедиться, что он адаптируется к istream_iterator.
источник
Использование range-v3 :
источник
Что - то вроде этого :
Пример использования:
Напечатает числа от 0 до 99
источник
Что ж, сегодня я тоже искал простую реализацию коллекции на C ++ 11. На самом деле я был разочарован, потому что все, что я нашел, слишком далеко от таких вещей, как генераторы Python или оператор yield C # ... или слишком сложно.
Цель состоит в том, чтобы создать коллекцию, которая будет выдавать свои элементы только тогда, когда это необходимо.
Я хотел, чтобы это было так:
Я нашел этот пост, ИМХО лучший ответ был о boost.coroutine2 от Yongwei Wu . Поскольку это ближе всего к тому, что хотел автор.
Стоит изучить программы ускорения .. И я, возможно, займусь на выходных. Но пока я использую свою очень маленькую реализацию. Надеюсь, это поможет кому-то другому.
Ниже приведен пример использования, а затем реализация.
Example.cpp
Generator.h
источник
Этот ответ работает на C (и, следовательно, я думаю, что он работает и на C ++)
Это простой, не объектно-ориентированный способ имитации генератора. У меня это сработало, как и ожидалось.
источник
Так же, как функция имитирует концепцию стека, генераторы имитируют концепцию очереди. Остальное - семантика.
Кстати, вы всегда можете смоделировать очередь со стеком, используя стек операций вместо данных. На практике это означает, что вы можете реализовать поведение, подобное очереди, путем возврата пары, второе значение которой либо имеет следующую функцию, которая должна быть вызвана, либо указывает, что у нас нет значений. Но это более общий характер, чем то, что делает доходность по сравнению с доходностью. Это позволяет моделировать очередь любых значений, а не однородных значений, которые вы ожидаете от генератора, но без сохранения полной внутренней очереди.
В частности, поскольку C ++ не имеет естественной абстракции для очереди, вам необходимо использовать конструкции, которые реализуют очередь внутри. Итак, ответ, который дал пример с итераторами, - достойная реализация концепции.
На практике это означает, что вы можете реализовать что-то с функциональностью простой очереди, если вам просто нужно что-то быстрое, а затем использовать значения очереди так же, как вы бы потребляли значения, полученные от генератора.
источник