AK_
AK_

Reputation: 8099

How to make an infinite sequence in C++

I'm using Visual Studio 2012 so C++11 is mostly OK... boost is also fine, but I would prefer to avoid other libreries, at least not widley used ones.

I want to create a forward only iterator that returns an infinite sequence, in the most elegant way possible. For example a sequence of all the natural numbers.

Basically I want the C++ equivilent of this f# code:

let nums =
    seq { while true do
            yield 1
            yield 2
        }

the above code basically creates an enumerator that returns [1;2;1;2...]

I know I could do this by writing a class, but there's got to be a shorter way with all the new lambdas and all...

Upvotes: 7

Views: 4437

Answers (6)

Germán Diago
Germán Diago

Reputation: 7663

The simpler thing, if you can depend on boost is to write something like this:

int i = 0;
auto gen = boost::make_generator_iterator([=]() mutable { return i++; });

C++14 version:

auto gen = boost::make_generator_iterator([i=0]() mutable { return i++;});

Documentation is here.

P.S.: I'm not sure if it will work without result_type member, which C++03 functor would need.

Upvotes: 5

Khurshid
Khurshid

Reputation: 2724

Here is C++14 index_sequence comes helping:

#include <iostream>
#include <vector>

namespace std
{
   template< int ...i> struct index_sequence{};

   template< int N, int ...i>
   struct make_seq_impl : make_seq_impl< N-1, N-1,i...> {};
   template< int ...i>
   struct make_seq_impl<0,i...>{ typedef index_sequence<i...> type; };

   template< int N > 
   using make_index_sequence  = typename make_seq_impl<N>::type;

} // namespace std


typedef std::vector<int>  integer_list;

template< typename F, int ...i >
integer_list make_periodic_list_impl(F f, std::index_sequence<i...> ) 
 {     
        //      {  1 2 1 2 1 2... }
        return {  f(i) ... };
 }

template< int N , typename F>
integer_list make_periodic_list(F f) 
{
   return make_periodic_list_impl(f, std::make_index_sequence<N>{} );
}



 int main()
 {
   std::vector<int> v = make_periodic_list<20>([](int i){return 1 + (i&1);}); 

     for( auto e : v ) std::cout << e << ' ';
 }

Upvotes: 0

Kiril Kirov
Kiril Kirov

Reputation: 38173

Is this what you want:

#include <iostream>
#include <vector>

int main() 
{
    auto nums = []
    {
        static unsigned x = 2;
        return ( x++ % 2 ) + 1;
    };

    std::vector< int > v{ nums(), nums(), nums(), nums(), nums() };
    for( auto i : v )
    {
        std::cout << i;
    }

    return 0;
}

or I have misunderstood the question?

Upvotes: 5

Sarfaraz Nawaz
Sarfaraz Nawaz

Reputation: 361482

I've written a library called Pipeline using which you can write such things easily, as:

auto infinite_seq = generate(1, [](int i) { return (i % 2) + 1; });

Now infinite_seq is a deferred-range which means it will generate the values and give you when you ask for it. If you ask for 10 values, it will generate exactly 10 values — this can be expressed as:

auto values = infinite_seq | take(10);

Or you can write this:

auto values = generate(1, [](int i) { return (i % 2) + 1; }) | take(10);

for(auto i : values) 
      //working with i

Have a look at the documentation of generate.

Upvotes: 3

Spook
Spook

Reputation: 25927

When you have a hammer in your hand, everything around looks like a nail. Lambdas and other C++11 features are sure cool, but you should choose valid tools for problems. In this case I see nothing simpler and more elegant than short class with overloaded operators:

class MyInfiniteIter
{
private:
    unsigned int i;

public:
    MyInfiniteIter()
    {
        i = 0;
    }

    int operator *() {

        return i;
    }

    int operator++ () {

        i++;
        if (i == 10)
            i = 0;

        return i;
    }
};

int main(int argc, char * argv[])
{
    for (MyInfiniteIter i;; ++i)
    {
        printf("%d\n", *i);
    }
}

Upvotes: 0

Konrad Rudolph
Konrad Rudolph

Reputation: 545608

Standard C++ has no real iterator generators which help you avoid writing the class manually. You can take a look at my range library for such an iterator generator to get going. This code essentially allows you to write

for (auto i : range(1))
    …

which generates the infinite sequence 1, 2, 3, …. Boost.Iterator contains tools for transforming one iterator output into another, related output. You could use that to repeatedly cycle over elements from a two-item container (containing the elements 1 and 2, in your case).

Upvotes: 2

Related Questions