7

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...

AK_
  • 7,981
  • 7
  • 46
  • 78
  • 6
    I suppose if you *really* don't want to write a class then you could use `boost::transform_iterator` applied to `boost::counting_iterator`. Lambdas provide anonymous functor types, they don't provide anonymous iterator types. So on their own they can't be used to define an iterator. – Steve Jessop Nov 28 '13 at 11:31
  • 1
    [Boost Coroutine](http://www.boost.org/doc/libs/1_55_0/libs/coroutine/doc/html/index.html) allows you writing code in the style of your f# sample. – ComicSansMS Nov 28 '13 at 11:38
  • 1
    Just a warning: C++ doesn't take well to infinity. It's inherently eager and strictly evaluated, which means you need to go out of your way to make it work nicely. – Xeo Nov 28 '13 at 12:12

6 Answers6

5

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?

Kiril Kirov
  • 37,467
  • 22
  • 115
  • 187
  • that's the general direction, but I want something that poses as a collection, that I could give to the STL. – AK_ Nov 28 '13 at 11:44
  • 1
    @AK_: there are few standard algorithms that make sense when passed an infinite repeating sequence. Which in particular are you planning to use? – Steve Jessop Nov 28 '13 at 11:53
  • @AK_ - what you mean by "to give to the STL"? – Kiril Kirov Nov 28 '13 at 11:56
  • @KirilKirov: BTW, you don't have to write `[]() -> int { ... }`. Just `[] {...}` would be enough in this case! – Nawaz Nov 28 '13 at 12:07
  • 1
    @AK_ - creating a `std::map` and using it for `std::transform` are pretty different - for STL container, you can use it in the initialization (as the lambda returns actual values), while `std::transform` _requires_ input iterator (something, that can be dereferenced and incremented) (see http://en.cppreference.com/w/cpp/algorithm/transform _second version_) – Kiril Kirov Nov 28 '13 at 12:15
  • 1
    @AK_: you can't create a map directly from an infinite sequence, because you can't have an infinite map. You could use `transform` provided that the output iterator is something that "consumes" the data, for example by printing it. But the only way out of `transform` would be by an exception. Basically, infinite ranges are not a great fit for standard algorithms, except `copy_n` :-) C++ algorithms aren't as nice as coroutines in this respect, they're harder to chain together. – Steve Jessop Nov 28 '13 at 12:17
  • 1
    @AK_: if by "map" you mean `std::map` then I assure you, it cannot be infinite :-) If you mean something lazily-evaluated of your own invention then you might be in business. – Steve Jessop Nov 28 '13 at 12:22
  • 1
    @AK_ - see my edit. You can't have an infinite container, as you don't have infinite memory on your PC, but it's still something.. About `std::transform` - it will be different there, because of the requirement for iterator. Than you can't avoid creating a custom one or using some existing (like boost's for example) – Kiril Kirov Nov 28 '13 at 12:32
  • @SteveJessop of course you can't have an infinite map. But you can use two sequences one finite and one infinite , and just stop when the finite one runs out... – AK_ Nov 28 '13 at 12:33
  • @KirilKirov I don't want a container. I want an infinite iterator. – AK_ Nov 28 '13 at 12:36
  • 1
    @Jarod42 - no, it's not unspecified, see http://stackoverflow.com/questions/20266153/stdinitializer-list-and-order-of-evaluation-of-the-elements – Kiril Kirov Nov 29 '13 at 07:42
5

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.

Germán Diago
  • 7,473
  • 1
  • 36
  • 59
3

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.

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • 1. Again, I prefer to avoid libraries that are not adopted by a large community. 2. I don't doubt your knowledge of C++ , but you make a lot of assertions about LINQ which are GROSSLY incorrect. – AK_ Nov 28 '13 at 12:57
  • @AK_: No problem. I don't claim it to be perfect either. Hope you get *perfect* solution. As for LINQ, it is just an inspiration, my library is NOT same and identical to LINQ. – Nawaz Nov 28 '13 at 13:00
  • ... I would like to know *why* you think LINQ is "*GROSSLY incorrect*"? – Nawaz Nov 28 '13 at 13:03
  • 1. your source code looks quite nice. 2. I would further suggest you would take a look at the examples in the RX-CPP library, they have some interesting ideas, not sure if all are good though. 3. I've sent you an EMail explaining what i didn't like about your comparison with linq. – AK_ Nov 28 '13 at 14:16
  • Is tuis really infinite? After `2^31-1` the `int` rolls over, and `%` behaves interesimgly on negative `int`, plus the entire overflow undefined issue... My infinity includes 5 billion. – Yakk - Adam Nevraumont Nov 29 '13 at 13:15
  • So the input is the last iteration's output? Not the index? – Yakk - Adam Nevraumont Nov 29 '13 at 13:37
  • @Yakk: Yes. :-) ... So the value of `i` is either `1` or `2`. Index-based approach would fail to produce infinite sequence, which is why it is avoided. – Nawaz Nov 29 '13 at 13:42
  • I also assume `for( auto i : generate(1, [](int i) { return (i % 2) + 1; }) )` works? ... expression seems neat, but I'd be tempted to make member pointers into expression modifiers. So `expression foo; auto age = member(&Foo::age); auto get_age = foo^age;` rather than `auto get_age = age;` -- so the `expression<>` always being the input. Would mimic LINQ and lambda and non-`expression` syntax in what I think is a good way... – Yakk - Adam Nevraumont Nov 29 '13 at 14:02
  • @Yakk: Yes, that `for` loop would work and it is an infinite loop. And I think you've typo here : *"... `auto get_age = foo^age;` rather than `auto get_age = age;`"* . So I couldn't understand the rest of the comment. Could you rephrase it? – Nawaz Nov 29 '13 at 14:41
  • @Nawaz No typo? `_m(&Foo::age)` in `expression` says "I expect 1 input that is a `Foo` and return the member field `age`". I'm saying that a member accessor that can be attached to an `expression` via `operator^` to extract the `age` field might make for more natural code. So instead of `auto extract_age = memexp(&person::get_age)` we have `expression Person; auto age = memacces(&person::get_age); auto extract_age = Person^age;`, where `extract_age` does the same thing in both cases. `memaccess` doesn't produce an expression, but an expression mutator. Just to get `p.age`-like syntax. – Yakk - Adam Nevraumont Nov 29 '13 at 15:53
  • @Yakk: That could be an alternative syntax. But I don't see any advantage (you're typing more on the contrary). `memexp` is just like `std::mem_fn`, and addtionally it also supports expression syntax; e.g `extract_age * 10` would produce a functor which will return `10` times of age. Anyway, if you're thinking of alternative syntax, then why not `auto extract_age = Person->age` instead, which looks more intuitive. – Nawaz Nov 29 '13 at 16:10
  • @Nawaz Because `->` is a unary operator, not a binary one. – Yakk - Adam Nevraumont Nov 29 '13 at 16:39
  • @Yakk: Still can be supported, using proxy object! – Nawaz Nov 29 '13 at 18:20
2

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).

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 1
    I've only briefly scanned your library code, but firstly doesn't it need to be `range(1U)` to avoid UB on overflow? Secondly does your `infinite_range_proxy` class support `boost::adaptors::transform(range(0U), [](unsigned i) { return (i % 2) + 1;})`? I think it does because it has `begin()` and `end()`, but if it needs anything else then it should add that :-) – Steve Jessop Nov 28 '13 at 11:47
  • +1. Your librery looks good, but I won't use it. I don't have the knowladge to review it... – AK_ Nov 28 '13 at 12:23
  • @SteveJessop I simply wouldn’t let it overflow … ;-) Normally you use infinite sequences as a start and then truncate them later. Secondly, I haven’t actually looked at interoperability with Boost.Iterator at all due to lack of time. I’d love to do that at some point though. – Konrad Rudolph Nov 28 '13 at 13:17
0

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);
    }
}
Spook
  • 25,318
  • 18
  • 90
  • 167
  • 1
    `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...` – Kiril Kirov Nov 28 '13 at 11:53
  • `new lambdas and all` doesn't seem to be a good choice here. Their pure existence does not justify using them anywhere, and in this particular case - in my opinion - simple class is the most elegant solution. – Spook Nov 28 '13 at 11:56
  • 1
    Agreed (in general), but the OP doesn't want classes for some reason. Could be even educational purpose, no idea. – Kiril Kirov Nov 28 '13 at 11:58
  • 2
    I think more to the point, this class *is not an iterator*. You must either specialize `iterator_traits` or include the typedefs that you get by inheriting from `std::iterator`. – Steve Jessop Nov 28 '13 at 12:08
  • Sure, but lambda is no iterator *even more* :) – Spook Nov 28 '13 at 12:19
  • @Spook Look at the F# example, look at your class (which entails an extra file BTW), which is more elegant? – AK_ Nov 28 '13 at 12:28
  • Comparing C++ to C# is like comparing apples to pineapples. C++ is way lower-level and some things are not as simple to be done as in C#. I still say, that *as for C++* this is the most elegant solution. Otherwise you always can implement it in F# and create some kind of wrapper to C++ - you'll get your #-style elegant solution... – Spook Nov 28 '13 at 12:41
  • C++ is definitely more "challenging" than C#, but regardless my example was from F#. C++ supports a LOT of functional style programming, and I don't see any reason why I should write a lot of boiler-tape pointless code, when I can achieve what I want with three lines. also C++ doesnt agree with you: http://stackoverflow.com/a/20265815/182360 – AK_ Nov 28 '13 at 14:23
  • And so what? Firstly, that boost:: stuff for sure returns you a class instance. Secondly, it could have used function pointer instead of lambda as well. Thirdly, these are external libraries *not* being a part of standard C++ library (quite popular though). How does it fit your problem description? – Spook Nov 29 '13 at 06:54
0

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 << ' ';
 }
Khurshid
  • 2,654
  • 2
  • 21
  • 29