Reputation: 21
I have to create class Iterator, itering sequence in a vector to complete a task, but in task is used range-based for loop like:
for (T element: sequence) {
std::cout << element << " ";
std::cin >> numToGenerate;
sequence.generateNext(numToGenerate);
}
So I should create my own Sequence and Iterator classes which can do the task, but I can not figure out how to do my end() iterator.
Here is my code till now:
template<typename T, typename Generator>
class Sequence
{
std::vector<T> elements;
Generator generator;
public:
class Iterator
{
public:
int index;
std::vector<T>& elements;
Iterator(int index, std::vector<T>& elements) : index(index), elements(elements) {};
void operator++()
{
this->index++;
}
T operator*()
{
return this->elements[this->index];
}
bool operator!= ( Iterator other)
{
return this->index != other.index;
}
};
void generateNext(int numToGenerate)
{
for (int i = 0; i < numToGenerate; i++)
{
T next = generator();
elements.push_back(next);
}
}
Iterator begin()
{
return Iterator(0, elements);
}
Iterator end()
{
//??????????
}
};
Upvotes: 2
Views: 885
Reputation: 1
I was gives exactly the same task this week and your code helped me a lot. You have probably already solved the task but I figured out that it doesn't matter what the end() returns - it can be the same as begin() or anything that compiles successfully. The key to the task is overloading operator != so it gets the new vector size each cycle. I did it by comparing the variable index with the size.
bool operator!= ( Iterator other)
{
return this->index != elements.size();
}
Upvotes: 0
Reputation: 21
include <iostream>
#include "Sequence.h"
class IntegersGenerator {
int last;
public:
IntegersGenerator() : last(0) {}
int operator()() {
return this->last++;
}
};
class FibonacciGenerator{
int last;
int current;
public:
FibonacciGenerator() : last(0), current(0) {}
int operator()() {
if (this->current == 0 && this->last == 0) {
this->last = 1;
return 0;
}
int next = this->last + this->current;
this->last = this->current;
this->current = next;
return next;
}
};
template<typename T, typename Generator>
void readGenerateWrite() {
Sequence<T, Generator> sequence;
int numToGenerate;
std::cin >> numToGenerate;
sequence.generateNext(numToGenerate);
for (T element : sequence) {
std::cout << element << " ";
std::cin >> numToGenerate;
sequence.generateNext(numToGenerate);
}
}
int main() {
char generatorType;
std::cin >> generatorType;
if (generatorType == 'i') {
readGenerateWrite<int, IntegersGenerator>();
}
else if (generatorType == 'f') {
readGenerateWrite<int, FibonacciGenerator>();
}
return 0;
}
That is the code given to me with the task, which I can not change.
and Example:
Input:
i
3 1 1 0 2 0 0 0
Output: 0 1 2 3 4 5 6
Upvotes: 0
Reputation: 122178
I have to say that I find your motivation rather questionable. Why do you want to push elements to the end while iterating over the elements?
Your generateNext
does not depend on the index. No matter at what index you are, you always push at the end (meaning your Generator
would have to remember what elements it already produced, ie keep an index, isnt this what the sequnce is supposed to do?).
Assuming your loop as written works, it can only end if the user types several times 0
until the sequence reaches the end. The natural way to insert element into your sequence is what you implemented in generateNext
. Why you want to intervene that with iterating over the sequence is not clear to me.
Anyhow, your loop can be made working if you make the comparsion to the end
iterator special (I left out the Generator
for the sake of brevity):
#include <iostream>
#include <vector>
template<typename T>
struct Sequence {
std::vector<T> elements;
struct Iterator {
int index;
std::vector<T>& elements;
bool isEnd;
Iterator(int index, std::vector<T>& elements,bool isEnd=false)
: index(index), elements(elements) {};
void operator++() { this->index++; }
T operator*() { return this->elements[this->index]; }
bool operator!= (const Iterator& other) {
if (other.isEnd) return index != elements.size();
return this->index != other.index;
}
};
void generateNext(int numToGenerate) {
static int counter = 0;
for (int i = 0; i < numToGenerate; i++) { elements.push_back(counter+i); }
counter += numToGenerate + 100;
}
Iterator begin() { return Iterator(0, elements); }
Iterator end() { return Iterator(0,elements,true); }
};
Now you can use the range based for loop:
int main() {
Sequence<int> s;
s.generateNext(5);
for (auto element : s) {
if (element <3) s.generateNext(3);
}
for (auto element : s) { std::cout << element << " ";}
}
Output:
0 1 2 3 4 105 106 107 208 209 210 311 312 313
Note that I specifically chose the "generator" such that you can see where in the loop the numbers are generated. Is this really what you want? Let me ask again: Why would you generate your sequnce in that way? Consider how the vector is filled:
first iteration: 0 1 2 3 4 105 106 107
^--------------------this one would be printed in your loop
second iteration: 0 1 2 3 4 105 106 107 208 209 210
^
third iteration: 0 1 2 3 4 105 106 107 208 209 210 311 312 313
^
and then some more iterations where we dont add elements to eventually reach the end
Bottomline: I strongly recommend you to rethink your design. for
loops typically indicate that you know before-hand how many iterations have to be done. Using iterators typically helps to be independent of the container type, but inside your loop you actually do use the container not the iterator. end
iterators are "fragile" in general, because if you push something then even without reallocation what was end
before now isnt anymore.
TL;DR: use a while-loop instead, something along the line of:
sequence s;
generator g;
while (g.has_more()) s.push_back(g());
Upvotes: 0
Reputation: 973
You can create the end iterator pointing to one position beyond the last element from your vector, just like how the 'std::vector''s iterator does.
Iterator end()
{
return Iterator(elements.size(), elements);
}
As an alternative, you could just use std::vector::iterator
, and create wrapper begin
and end
methods for it.
Upvotes: 1