Я чувствую, что этот вопрос, должно быть, задавался и решался много раз, потому что это кажется мне довольно общим сценарием, но я не мог найти ничего, что указывало бы мне в направлении решения.
Я пытаюсь реализовать универсальный итерируемый Generator
объект, который производит последовательность чисел до тех пор, пока не будет выполнено определенное условие завершения, сигнализирующее, что такое условие было достигнуто для остановки итерации.
Основная идея, по сути, заключается в том, чтобы иметь нечто похожее на генераторы Python, где объект возвращает значения до тех пор, пока у него больше нет выхода, а затем возникает исключение StopIteration
для информирования внешнего цикла о завершении последовательности.
Из того, что я понимаю, проблема распадается на создание объекта, генерирующего последовательность, а затем получение итератора над ним.
Для объекта, генерирующего последовательность, я подумал, что определю базовый класс Generator
, который затем расширяется для обеспечения определенного поведения (например, для получения значений из набора диапазонов или из списка фиксированных значений и т. Д.). ). Все Generaor
s выдают новое значение при каждом вызове operator()
или выдают ValuesFinishedException
, если генератор работал до конца последовательности.
Я реализовал это как таковой (я показываю подкласс одного диапазона в качестве примера, но мне нужно иметь возможность моделировать больше типов последовательностей):
struct ValuesFinishedException : public std::exception { };
template <typename T>
class Generator
{
public:
Generator() { };
~Generator() { };
virtual T operator()() = 0; // return the new number or raise a ValuesFinishedException
};
template <typename T>
class RangeGenerator : public Generator<T>
{
private:
T m_start;
T m_stop;
T m_step;
T m_next_val;
public:
RangeGenerator(T start, T stop, T step) :
m_start(start),
m_stop(stop),
m_step(step),
m_next_val(start)
{ }
T operator()() override
{
if (m_next_val >= m_stop)
throw ValuesFinishedException();
T retval = m_next_val;
m_next_val += m_step;
return retval;
}
void setStep(T step) { m_step = step; }
T step() { return m_step; }
};
Что касается итераторов, я застрял.
Я исследовал любую комбинацию «Итератор», «Генератор» и синонимов, которую я только мог придумать, но все, что я нашел, рассматривает только случай, когда функция генератора имеет неограниченное количество значений (см., Например, generator_iterator boost ). Я думал о написании Generator::iterator
класса сам, но я нашел только примеры тривиальных итераторов (связанные списки, переопределения массивов), где end
четко определено. Я не знаю заранее, когда будет достигнут конец, я только знаю, что если генератор, который я перебираю, вызывает исключение, мне нужно установить текущее значение итератора в «end ()», но я не знать, как это представить.
Редактировать: добавление предполагаемого варианта использования
Причина этого класса в том, чтобы иметь гибкий объект последовательности, который я могу зациклить:
RangeGenerator gen(0.25f, 95.3f, 1.2f);
for(auto v : gen)
{
// do something with v
}
Пример диапазона - самый простой. У меня будет как минимум три фактических варианта использования:
- простой диапазон (с переменным шагом)
- объединение нескольких диапазонов
- последовательность постоянных значений, хранящихся в векторе
Для каждого из них я планирую иметь подкласс Generator
, с итератором, определенным для аннотации Generator
.