Implementation of python itertools and builtin iteration functions for C++17

Overview

CPPItertools

Range-based for loop add-ons inspired by the Python builtins and itertools library. Like itertools and the Python3 builtins, this library uses lazy evaluation wherever possible.

Note: Everything is inside the iter namespace.

Follow @cppitertools for updates.

Build and Test Status

Status Compilers
Travis Build Status gcc-7 gcc-8 gcc-9 clang-5.0 clang-6.0 clang-7 clang-8 clang-9
Appveyor Build Status MSVC 2017 MSVC 2019

Table of Contents

range
enumerate
zip
zip_longest
imap
filter
filterfalse
unique_everseen
unique_justseen
takewhile
dropwhile
cycle
repeat
count
groupby
starmap
accumulate
compress
sorted
chain
chain.from_iterable
reversed
slice
sliding_window
chunked
batched

Combinatoric fuctions

product
combinations
combinations_with_replacement
permutations
powerset

Requirements

This library is header-only and relies only on the C++ standard library. The only exception is zip_longest which uses boost::optional. #include <cppitertools/itertools.hpp> will include all of the provided tools except for zip_longest which must be included separately. You may also include individual pieces with the relevant header (#include <cppitertools/enumerate.hpp> for example).

Running tests

You may use either scons or bazel to build the tests. scons seems to work better with viewing the test output, but the same bazel command can be run from any directory.

To run tests with scons you must be within the test directory

test$ # build and run all tests
test$ scons
test$ ./test_all
test$ # build and run a specific test
test$ scons test_enumerate
test$ ./test_enumerate
test$ valgrind ./test_enumerate

bazel absolute commands can be run from any directory inside the project

$ bazel test //test:all # runs all tests
$ bazel test //test:test_enumerate # runs a specific test

Requirements of passed objects

Most itertools will work with iterables using InputIterators and not copy or move any underlying elements. The itertools that need ForwardIterators or have additional requirements are noted in this document. However, the cases should be fairly obvious: any time an element needs to appear multiple times (as in combinations or cycle) or be looked at more than once (specifically, sorted). This library takes every effort to rely on as little as possible from the underlying iterables, but if anything noteworthy is needed it is described in this document.

Guarantees of implementations

By implementations, I mean the objects returned by the API's functions. All of the implementation classes are move-constructible, not copy-constructible, not assignable. All iterators that work over another iterable are tagged as InputIterators and behave as such.

Feedback

If you find anything not working as you expect, not compiling when you believe it should, a divergence from the python itertools behavior, or any sort of error, please let me know. The preferable means would be to open an issue on GitHub. If you want to talk about an issue that you don't feel would be appropriate as a GitHub issue (or you just don't want to open one), you can email me directly with whatever code you have that describes the problem; I've been pretty responsive in the past. If I believe you are "misusing" the library, I'll try to put the blame on myself for being unclear in this document and take the steps to clarify it. So please, contact me with any concerns, I'm open to feedback.

How (not) to use this library

The library functions create and return objects that are properly templated on the iterable they are passed. These exact names of these types or precisely how they are templated is unspecified, you should rely on the functions described in this document. If you plan to use these functions in very simple, straight forward means as in the examples on this page, then you will be fine. If you feel like you need to open the header files, then I've probably under-described something, let me know.

Handling of rvalues vs lvalues

The rules are pretty simple, and the library can be largely used without knowledge of them. Let's take an example

std::vector<int> vec{2,4,6,8};
for (auto&& p : enumerate(vec)) { /* ... */ }

In this case, enumerate will return an object that has bound a reference to vec. No copies are produced here, neither of vec nor of the elements it holds.

If an rvalue was passed to enumerate, binding a reference would be unsafe. Consider:

for (auto&& p : enumerate(std::vector<int>{2,4,6,8})) { /* ... */ }

Instead, enumerate will return an object that has the temporary moved into it. That is, the returned object will contain a std::vector<int> rather than just a reference to one. This may seem like a contrived example, but it matters when enumerate is passed the result of a function call like enumerate(f()), or, more obviously, something like enumerate(zip(a, b)). The object returned from zip must be moved into the enumerate object. As a more specific result, itertools can be mixed and nested.

Pipe syntax

Wherever it makes sense, I've implemented the "pipe" operator that has become common in similar libraries. When the syntax is available, it is done by pulling out the iterable from the call and placing it before the tool. For example:

filter(pred, seq);  // regular call
seq | filter(pred);  // pipe-style
enumerate(seq);  // regular call
seq | enumerate;  // pipe-style.

The following tools support pipe. The remaining I left out because although some of them have multiple reasonable versions, it wasn't obvious to me how I would expect them to behave:

  • accumulate
  • chain.from_iterable
  • chunked
  • batched
  • combinations
  • combinations_with_replacement
  • cycle
  • dropwhile
  • enumerate
  • filter
  • filterfalse
  • groupby
  • imap
  • permutations
  • powerset
  • reversed
  • slice
  • sliding_window
  • sorted
  • starmap
  • takewhile
  • unique_everseen
  • unique_justseen

I don't personally care for the piping style, but it seemed to be desired by the users.

range

Uses an underlying iterator to achieve the same effect of the python range function. range can be used in three different ways:

Only the stopping point is provided. Prints 0 1 2 3 4 5 6 7 8 9

for (auto i : range(10)) {
    cout << i << '\n';
}

The start and stop are both provided. Prints 10 11 12 13 14

for (auto i : range(10, 15)) {
    cout << i << '\n';
}

The start, stop, and step are all provided. Prints 20 22 24 26 28

for (auto i : range(20, 30, 2)) {
    cout << i << '\n';
}

Negative values are allowed as well. Prints 2 1 0 -1 -2

for (auto i : range(2, -3, -1)) {
    cout << i << '\n';
}

A step size of 0 results in an empty range (Python's raises an exception). The following prints nothing

for (auto i : range(0, 10, 0)) {
    cout << i << '\n';
}

In addition to normal integer range operations, doubles and other numeric types are supported through the template

Prints: 5.0 5.5 6.0 ... 9.5

for(auto i : range(5.0, 10.0, 0.5)) {
    cout << i << '\n';
}

Implementation Note: Typical ranges have their current value incremented by the step size repeatedly (value += step). Floating point range value are recomputed at each step to avoid accumulating floating point inaccuracies (value = start + (step * steps_taken). The result of the latter is a bit slower but more accurate.

range also supports the following operations:

  • .size() to get the number of elements in the range (not enabled for floating point ranges).
  • Accessors for .start(), .stop(), and .step().
  • Indexing. Given a range r, r[n] is the nth element in the range.

enumerate

Continually "yields" containers similar to pairs. They are basic structs with a .index and a .element, and also work with structured binding declarations. Usage appears as:

vector<int> vec{2, 4, 6, 8};
for (auto&& [i, e] : enumerate(vec)) {
    cout << i << ": " << e << '\n';
}

filter

Called as filter(predicate, iterable). The predicate can be any callable. filter will only yield values that are true under the predicate.

Prints values greater than 4: 5 6 7 8

vector<int> vec{1, 5, 4, 0, 6, 7, 3, 0, 2, 8, 3, 2, 1};
for (auto&& i : filter([] (int i) { return i > 4; }, vec)) {
    cout << i <<'\n';
}

If no predicate is passed, the elements themselves are tested for truth

Prints only non-zero values.

for(auto&& i : filter(vec)) {
    cout << i << '\n';
}

filterfalse

Similar to filter, but only prints values that are false under the predicate.

Prints values not greater than 4: 1 4 3 2 3 2 1

vector<int> vec{1, 5, 4, 0, 6, 7, 3, 0, 2, 8, 3, 2, 1};
for (auto&& i : filterfalse([] (int i) { return i > 4; }, vec)) {
    cout << i <<'\n';
}

If no predicate is passed, the elements themselves are tested for truth.

Prints only zero values.

for(auto&& i : filterfalse(vec)) {
    cout << i << '\n';
}

unique_everseen

Additional Requirements: Underlying values must be copy-constructible.

This is a filter adaptor that only generates values that have never been seen before. For this to work your object must be specialized for std::hash.

Prints 1 2 3 4 5 6 7 8 9

vector<int> v {1,2,3,4,3,2,1,5,6,7,7,8,9,8,9,6};
for (auto&& i : unique_everseen(v)) {
    cout << i << ' ';
}

unique_justseen

Another filter adaptor that only omits consecutive duplicates.

Prints 1 2 3 4 3 2 1 Example Usage:

vector<int> v {1,1,1,2,2,3,3,3,4,3,2,1,1,1};
for (auto&& i : unique_justseen(v)) {
    cout << i << ' ';
}

takewhile

Yields elements from an iterable until the first element that is false under the predicate is encountered.

Prints 1 2 3 4. (5 is false under the predicate)

vector<int> ivec{1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1};
for (auto&& i : takewhile([] (int i) {return i < 5;}, ivec)) {
    cout << i << '\n';
}

dropwhile

Yields all elements after and including the first element that is true under the predicate.

Prints 5 6 7 1 2

vector<int> ivec{1, 2, 3, 4, 5, 6, 7, 1, 2};
for (auto&& i : dropwhile([] (int i) {return i < 5;}, ivec)) {
    cout << i << '\n';
}

cycle

Additional Requirements: Input must have a ForwardIterator

Repeatedly produces all values of an iterable. The loop will be infinite, so a break or other control flow structure is necessary to exit.

Prints 1 2 3 repeatedly until some_condition is true

vector<int> vec{1, 2, 3};
for (auto&& i : cycle(vec)) {
    cout << i << '\n';
    if (some_condition) {
        break;
    }
}

repeat

Repeatedly produces a single argument forever, or a given number of times. repeat will bind a reference when passed an lvalue and move when given an rvalue. It will then yield a reference to the same item until completion.

The below prints 1 five times.

for (auto&& e : repeat(1, 5)) {
    cout << e << '\n';
}

The below prints 2 forever

for (auto&& e : repeat(2)) {
    cout << e << '\n';
}

count

Effectively a range without a stopping point.
count() with no arguments will start counting from 0 with a positive step of 1.
count(i) will start counting from i with a positive step of 1.
count(i, st) will start counting from i with a step of st.

Technical limitations: Unlike Python which can use its long integer types when needed, count() would eventually exceed the maximum possible value for its type (or minimum with a negative step). count is actually implemented as a range with the stopping point being the std::numeric_limits<T>::max() for the integral type (long by default)

The below will print 0 1 2 ... etc

for (auto&& i : count()) {
    cout << i << '\n';
}

groupby

Additional Requirements: If the Input's iterator's operator*() returns a reference, the reference must remain valid after the iterator is incremented. Roughly equivalent to requiring the Input have a ForwardIterator.

Separate an iterable into groups sharing a common key. The following example creates a new group whenever a string of a different length is encountered.

vector<string> vec = {
    "hi", "ab", "ho",
    "abc", "def",
    "abcde", "efghi"
};

for (auto&& gb : groupby(vec, [] (const string &s) {return s.length(); })) {
    cout << "key: " << gb.first << '\n';
    cout << "content: ";
    for (auto&& s : gb.second) {
        cout << s << "  ";
    }
    cout << '\n';
}

Note: Just like Python's itertools.groupby, this doesn't do any sorting. It just iterates through, making a new group each time there is a key change. Thus, if the group is unsorted, the same key may appear multiple times.

starmap

Takes a sequence of tuple-like objects (anything that works with std::get) and unpacks each object into individual arguments for each function call. The below example takes a vector of pairs of ints, and passes them to a function expecting two ints, with the elements of the pair being the first and second arguments to the function.

vector<pair<int, int>> v = {{2, 3}, {5, 2}, {3, 4}}; // {base, exponent}
for (auto&& i : starmap([](int b, int e){return pow(b, e);}, v)) {
    // ...
}

starmap can also work over a tuple-like object of tuple-like objects even when the contained objects are different as long as the functor works with multiple types of calls. For example, a Callable struct with overloads for its operator() will work as long as all overloads have the same return type

struct Callable {
    int operator()(int i) const;
    int operator()(int i, char c) const;
    int operator()(double d, int i, char c) const;
};

This will work with a tuple of mixed types

auto t = make_tuple(
        make_tuple(5), // first form
        make_pair(3, 'c'), // second
        make_tuple(1.0, 1, '1')); // third
for (auto&& i : starmap(Callable{}, t)) {
    // ...
}

accumulate

Additional Requirements: Type return from functor (with reference removed) must be assignable.

Differs from std::accumulate (which in my humble opinion should be named std::reduce or std::foldl). It is similar to a functional reduce where one can see all of the intermediate results. By default, it keeps a running sum. Prints: 1 3 6 10 15

for (auto&& i : accumulate(range(1, 6))) {
    cout << i << '\n';
}

A second, optional argument may provide an alternative binary function to compute results. The following example multiplies the numbers, rather than adding them. Prints: 1 2 6 24 120

for (auto&& i : accumulate(range(1, 6), std::multiplies<int>{})) {
    cout << i << '\n';
}

Note: The intermediate result type must support default construction and assignment.

zip

Takes an arbitrary number of ranges of different types and efficiently iterates over them in parallel (so an iterator to each container is incremented simultaneously). When you dereference an iterator to "zipped" range you get a tuple of the elements the iterators were holding.

Example usage:

array<int,4> iseq{{1,2,3,4}};
vector<float> fseq{1.2,1.4,12.3,4.5,9.9};
vector<string> sseq{"i","like","apples","a lot","dude"};
array<double,5> dseq{{1.2,1.2,1.2,1.2,1.2}};

for (auto&& [i, f, s, d] : zip(iseq, fseq, sseq, dseq)) {
    cout << i << ' ' << f << ' ' << s << ' ' << d << '\n';
    f = 2.2f; // modifies the underlying 'fseq' sequence
}

zip_longest

Terminates on the longest sequence instead of the shortest. Repeatedly yields a tuple of boost::optional<T>s where T is the type yielded by the sequences' respective iterators. Because of its boost dependency, zip_longest is not in itertools.hpp and must be included separately. The following loop prints either "Just " or "Nothing" for each element in each tuple yielded.

vector<int> v1 = {0, 1, 2, 3};
vector<int> v2 = {10, 11};
for (auto&& [x, y] : zip_longest(v1, v2)) {
    cout << '{';
    if (x) {
        cout << "Just " << *x;
    } else {
        cout << "Nothing";
    }
    cout << ", ";
    if (y) {
        cout << "Just " << *y;
    } else {
        cout << "Nothing";
    }
    cout << "}\n";
}

The output is:

{Just 0, Just 10}
{Just 1, Just 11}
{Just 2, Nothing}
{Just 3, Nothing}

imap

Takes a function and one or more iterables. The number of iterables must match the number of arguments to the function. Applies the function to each element (or elements) in the iterable(s). Terminates on the shortest sequence.

Prints the squares of the numbers in vec: 1 4 9 16 25

vector<int> vec{1, 2, 3, 4, 5};
for (auto&& i : imap([] (int x) {return x * x;}, vec)) {
    cout << i << '\n';
}

With more than one sequence, the below adds corresponding elements from each vector together, printing 11 23 35 47 59 71

vector<int> vec1{1, 3, 5, 7, 9, 11};
vector<int> vec2{10, 20, 30, 40, 50, 60};
for (auto&& i : imap([] (int x, int y) { return x + y; }, vec1, vec2)) {
    cout << i << '\n';
}

Note: The name imap is chosen to prevent confusion/collision with std::map, and because it is more related to itertools.imap than the python builtin map.

compress

Yields only the values corresponding to true in the selectors iterable. Terminates on the shortest sequence.

Prints 2 6

vector<int> ivec{1, 2, 3, 4, 5, 6};
vector<bool> bvec{false, true, false, false, false, true};
for (auto&& i : compress(ivec, bvec) {
    cout << i << '\n';
}

sorted

Additional Requirements: Input must have a ForwardIterator

Allows iteration over a sequence in sorted order. sorted does not produce a new sequence, copy elements, or modify the original sequence. It only provides a way to iterate over existing elements. sorted also takes an optional second comparator argument. If not provided, defaults to std::less.
Iterables passed to sorted are required to have an iterator with an operator*() const member.

The below outputs 0 1 2 3 4.

unordered_set<int> nums{4, 0, 2, 1, 3};
for (auto&& i : sorted(nums)) {
    cout << i << '\n';
}

chain

Additional Requirements: The underlying iterators of all containers' operator* must have the exact same type

This can chain any set of ranges together as long as their iterators dereference to the same type.

vector<int> empty{};
vector<int> vec1{1,2,3,4,5,6};
array<int,4> arr1{{7,8,9,10}};

for (auto&& i : chain(empty,vec1,arr1)) {
    cout << i << '\n';
}

chain.from_iterable

Similar to chain, but rather than taking a variadic number of iterables, it takes an iterable of iterables and chains the contained iterables together. A simple example is shown below using a vector of vectors to represent a 2d ragged array, and prints it in row-major order.

vector<vector<int>> matrix = {
    {1, 2, 3},
    {4, 5},
    {6, 8, 9, 10, 11, 12}
};

for (auto&& i : chain.from_iterable(matrix)) {
    cout << i << '\n';
}

reversed

Additional Requirements: Input must be compatible with std::rbegin() and std::rend()

Iterates over elements of a sequence in reverse order.

for (auto&& i : reversed(a)) {
    cout << i << '\n';
}

slice

Returns selected elements from a range, parameters are start, stop and step. the range returned is [start,stop) where you only take every step element

This outputs 0 3 6 9 12

vector<int> a{0,1,2,3,4,5,6,7,8,9,10,11,12,13};
for (auto&& i : slice(a,0,15,3)) {
    cout << i << '\n';
}

sliding_window

Additional Requirements: Input must have a ForwardIterator

Takes a section from a range and increments the whole section. If the window size is larger than the length of the input, the sliding_window will yield nothing (begin == end).

Example: [1, 2, 3, 4, 5, 6, 7, 8, 9]

take a section of size 4, output is:

1 2 3 4
2 3 4 5
3 4 5 6
4 5 6 7
5 6 7 8
6 7 8 9

Example Usage:

vector<int> v = {1,2,3,4,5,6,7,8,9};
for (auto&& sec : sliding_window(v,4)) {
    for (auto&& i : sec) {
        cout << i << ' ';
        i.get() = 90;
    }
    cout << '\n';
}

chunked

chunked will yield subsequent chunks of an iterable in blocks of a specified size. The final chunk may be shorter than the rest if the chunk size given does not evenly divide the length of the iterable.

Example usage:

vector<int> v {1,2,3,4,5,6,7,8,9};
for (auto&& sec : chunked(v,4)) {
    for (auto&& i : sec) {
        cout << i << ' ';
    }
    cout << '\n';
}

The above prints:

1 2 3 4
5 6 7 8
9

batched

batched will yield a given number N of batches containing subsequent elements from an iterable, assuming the iterable contains at least N elements. The size of each batch is immaterial, but the implementation guarantees that no two batches will differ in size by more than 1.

Example usage:

vector<int> v {1,2,3,4,5,6,7,8,9};
for (auto&& sec : batched(v,4)) {
    for (auto&& i : sec) {
        cout << i << ' ';
    }
    cout << '\n';
}

The above prints:

1 2 3
4 5
6 7
8 9

product

Additional Requirements: Input must have a ForwardIterator

Generates the cartesian product of the given ranges put together.

Example usage:

vector<int> v1{1,2,3};
vector<int> v2{7,8};
vector<string> v3{"the","cat"};
vector<string> v4{"hi","what's","up","dude"};
for (auto&& [a, b, c, d] : product(v1,v2,v3,v4)) {
    cout << a << ", " << b << ", " << c << ", " << d << '\n';
}

Product also accepts a "repeat" as a template argument. Currently this is the only way to do repeats. If you are reading this and need product(seq, 3) instead of product<3>(seq) please open an issue.

Example usage:

std::string s = "abc";
// equivalent of product(s, s, s);
for (auto&& t : product<3>(s)) {
   // ...
}

combinations

Additional Requirements: Input must have a ForwardIterator

Generates n length unique sequences of the input range.

Example usage:

vector<int> v = {1,2,3,4,5};
for (auto&& i : combinations(v,3)) {
    for (auto&& j : i ) cout << j << " ";
    cout << '\n';
}

combinations_with_replacement

Additional Requirements: Input must have a ForwardIterator

Like combinations, but with replacement of each element. The below is printed by the loop that follows:

{A, A}
{A, B}
{A, C}
{B, B}
{B, C}
{C, C}
for (auto&& v : combinations_with_replacement(s, 2)) {
    cout << '{' << v[0] << ", " << v[1] << "}\n";
}

permutations

Additional Requirements: Input must have a ForwardIterator. Iterator must have an operator*() const.

Generates all the permutations of a range using std::next_permutation.

Example usage:

vector<int> v = {1,2,3,4,5};
for (auto&& vec : permutations(v)) {
    for (auto&& i : vec) {
        cout << i << ' ';
    }
    cout << '\n';
}

powerset

Additional Requirements: Input must have a ForwardIterator

Generates every possible subset of a set, runs in O(2^n).

Example usage:

vector<int> vec {1,2,3,4,5,6,7,8,9};
for (auto&& v : powerset(vec)) {
    for (auto&& i : v) {
        cout << i << " ";
    }
    cout << '\n';
}
Comments
  • Consider overloading the

    Consider overloading the "pipe" operator

    Hi,

    Often, I find myself mixing together cppitertools and boost.range (and – more recently – range-v3). I believe that supporting the (de-facto standard) pipe operator | would greatly improve the interoperability between the different range libraries.

    Thanks.

    enhancement 
    opened by iliocatallo 24
  • Support for std::begin & std::end

    Support for std::begin & std::end

    I have noted that your the container class must have a .begin() method otherwise you cannot declare the iterators. However, if you use declvar any iterable that support std::begin() (like static arrays) will also work in your framework. For instance, here's how I would declare a typedef for iterator_type:

    typedef decltype(std::begin(std::declval<Iterable&>())) iterator_type;
    

    Now the iterable can be:

    • vector
    • static array
    • map ..
    enhancement 
    opened by nova77 16
  • combination of chunked -> filter -> imap = bogus results

    combination of chunked -> filter -> imap = bogus results

    Hi

    The following code is a minimal reproducer of an issue I'm getting.

    vector<int16_t> v(10000, 0);
      auto a = iter::chunked(v,480);
      auto b = iter::filter([](auto & chunk){ return true;}, a);
      auto c = iter::imap([](auto & chunk){ cout << chunk.size() << " "; return 1;}, b);
      for(auto &&x: c);
    

    prints

    18446738293966284791 480 480 480 480 480 480 480 480 480 480 480 480 480 480 480 480 480 480 480 400

    The first chunk size is always bogus. Inside the first chunk there are also bogus values.

    Is this a bug, or I'm I doing something wrong ?

    bug 
    opened by miguel-negrao 15
  • Feature request: Something like list comprehension

    Feature request: Something like list comprehension

    First, I love this library. As soon as I first read of range-based for loops, I wanted this. Now, the feature request...

    It would be great to have a way to transform all of the elements in an iterator, as is often done with list comprehensions in python.

    for (auto&& p : transform([](int x) { return x*x; }, range(10))) { /* ... */ }

    or

    for (auto&& p : range(10) | transform([](int x) { return x*x; })) { /* ... */ }

    If there is some magic in C++ to do this already, perhaps that can go as an example in the readme.

    opened by adamsd5 12
  • imap function applied multiple times

    imap function applied multiple times

    I am trying to translate this (simplified) example from python (2.7) to C++ (tried both clang 3.4 and gcc 4.7) using move-only objects.

    Python snippet, prints 41 43:

    import itertools as it
    
    class MyItem:
        def __init__(self, val):
            self.val = val
        def with_val(self, val):
            self.val = val
            return self
    
    if "__main__" == __name__:
        vec = [MyItem(41), MyItem(42), MyItem(43)]
        transformed1 = it.imap(lambda el: el.with_val(el.val + 10), vec)
        filtered = it.ifilter(lambda el: 52 != el.val, transformed1)
        transformed2 = it.imap(lambda el: el.with_val(el.val - 10), filtered)
    
        for el in transformed2:
            print(el.val)
    

    C++ snippet, prints 51 53:

    #include <iostream>
    #include <vector>
    
    #include "imap.hpp"
    #include "filter.hpp"
    
    class MyMoveOnly {
        int val;
    public:
        MyMoveOnly() { };
        MyMoveOnly(int val) : val(val) { }
        MyMoveOnly(const MyMoveOnly&) = delete;
        MyMoveOnly& operator=(const MyMoveOnly&) = delete;
        MyMoveOnly(MyMoveOnly&& other) : val(other.val) { }
        MyMoveOnly& operator=(MyMoveOnly&& other) {
            this->val = other.val;
            return *this;
        }
        int get_val() {
            return val;
        }
        void set_val(int val) {
            this->val = val;        
        }
    };
    
    namespace it = iter;
    
    int main() {
        // source data
        std::vector<MyMoveOnly> vec{};
        vec.emplace_back(MyMoveOnly(41));
        vec.emplace_back(MyMoveOnly(42));
        vec.emplace_back(MyMoveOnly(43));
        // some transformations
        auto transformed1 = it::imap([](MyMoveOnly& el) {
            int va = el.get_val();
            el.set_val(va + 10);
            return std::move(el);
        }, vec);
        // r-value ref is used here because l-value ref won't compile
        auto filtered = it::filter([](MyMoveOnly&& el) {
            return 52 != el.get_val();
        }, transformed1);
        auto transformed2 = it::imap([](MyMoveOnly& el) {
            int va = el.get_val();
            el.set_val(va - 10);
            return std::move(el);
        }, filtered);
        // print data
        for (auto&& el : transformed2) {
            std::cout << el.get_val() << std::endl;
        }
    
        return 0;
    }
    

    I wasn't sure that this example will compile (because of non-copyable objects), it actually compiles and runs, but the first transformation is applied twice for each element. I am not sure whether this is intended behaviour, but it is definitely contr-intuitive because single foreach over the transformed sequence caused multiple applyings of imap function.

    opened by staticlibs 12
  • Support for move only types

    Support for move only types

    Hi,

    I would like to use the library with move only types. Here's a toy example:

    #include <iostream>
    #include <vector>
    #include <itertools/product.hpp>
    
    struct MoveOnlyType {
        MoveOnlyType() = default;
        MoveOnlyType(MoveOnlyType const& other) = delete;
        MoveOnlyType(MoveOnlyType&& other) = default;
        MoveOnlyType& operator=(MoveOnlyType const& other) = delete;
        MoveOnlyType& operator=(MoveOnlyType&& other) = default;
    };
    
    int main() {
    
        std::vector<MoveOnlyType> v1, v2;
        v1.push_back(MoveOnlyType());
        v1.push_back(MoveOnlyType());
    
        v2.push_back(MoveOnlyType());
        v2.push_back(MoveOnlyType());
        v2.push_back(MoveOnlyType());
    
    
        for (auto&& pair_of_moveonly: iter::product(v1, v2)) 
            std::cout << "it does not work" << std::endl;
    }
    

    Thanks.

    enhancement 
    opened by iliocatallo 12
  • Can't dereference const zip iterator

    Can't dereference const zip iterator

    #include <vector>
    
    #include "zip.hpp"
    
    int main()
    {
        std::vector<int> a(1);
    
        const auto it = iter::zip(a).begin();
        const auto [v] = *it;
    
        return v;
    }
    

    [[email protected] cppitertools]$ clang++ const_iter.cpp -I ./ -std=c++17 -fno-permissive const_iter.cpp:11:22: error: indirection requires pointer operand ('const iter::impl::Zipped<std::tuple<std::vector<int, std::allocator > &>, 0>::Iterator<std::tuple<std::vector<int, std::allocator > &>, iterator_tuple_type, iterator_deref_tuple>' invalid) const auto [v] = *it;

    Is there a reason for Zipped::Iterator::operator*() not to be const? https://github.com/ryanhaining/cppitertools/blob/master/zip.hpp#L90

    opened by ilyalesokhin-starkware 11
  • New proposed tool: batched

    New proposed tool: batched

    First off, great library! It's been very useful to me on several occasions, and it's written in high-quality C++. I recently needed a functionality similar to chunked, but where the number of chunks returned is specified, not the size of each chunk. I developed a solution that I called batched, and thought you'd find it a useful addition. As explained in the README, here's some typical use:

    vector<int> v {1,2,3,4,5,6,7,8,9};
    for (auto&& sec : batched(v,4)) {
        for (auto&& i : sec) {
            cout << i << ' ';
        }
        cout << '\n';
    }
    

    The above returns 4 batches, whose sizes are not specified, but are such that no two batches differ in size by more than one:

    1 2 3
    4 5
    6 7
    8 9
    

    The implementation is quite similar to chunked, and is pretty straightforward. The only extra complication is the class template distance_helper (and its specialization), which is necessary to find the container size (needed by the algorithm) in the case of different begin and end iterator types.

    Let me know if any questions or suggestions. BTW, I'm not married to the name batched, so please feel free to suggest a better alternative if you see fit. Thanks!

    opened by ssbotelh 8
  • zip.hpp doesn't compile with gcc 8.2.1

    zip.hpp doesn't compile with gcc 8.2.1

    $ g++ examples/zip_examples.cpp -I ./ -std=c++17 In file included from examples/zip_examples.cpp:1: ./zip.hpp: In instantiation of ‘class iter::impl::Zipped<std::tuple<std::vector<int, std::allocator >&, std::vector<std::__cxx11::basic_string<char, std::char_traits, std::allocator >, std::allocator<std::__cxx11::basic_string<char, std::char_traits, std::allocator > > >&>, 0, 1>::Iterator<std::tuple<std::vector<int, std::allocator >&, std::vector<std::__cxx11::basic_string<char, std::char_traits, std::allocator >, std::allocator<std::__cxx11::basic_string<char, std::char_traits, std::allocator > > >&>, iter::impl::iterator_tuple_type, iter::impl::iterator_deref_tuple>’: examples/zip_examples.cpp:13:46: required from here ./zip.hpp:41:60: error: template parameter ‘template<class TupleType, long unsigned int ...Is> template class IteratorTuple’ template <typename TupleTypeT, template class IteratorTuple, ^~~~~~~~~~~~~ ./zip.hpp:46:18: error: redeclared here as ‘template class’ friend class Iterator; ^~~~~~~~

    $ g++ -v Using built-in specs. COLLECT_GCC=g++ COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-linux-gnu/8.2.1/lto-wrapper Target: x86_64-pc-linux-gnu Configured with: /build/gcc/src/gcc/configure --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugs.archlinux.org/ --enable-languages=c,c++,ada,fortran,go,lto,objc,obj-c++ --enable-shared --enable-threads=posix --enable-libmpx --with-system-zlib --with-isl --enable-__cxa_atexit --disable-libunwind-exceptions --enable-clocale=gnu --disable-libstdcxx-pch --disable-libssp --enable-gnu-unique-object --enable-linker-build-id --enable-lto --enable-plugin --enable-install-libiberty --with-linker-hash-style=gnu --enable-gnu-indirect-function --enable-multilib --disable-werror --enable-checking=release --enable-default-pie --enable-default-ssp --enable-cet=auto Thread model: posix gcc version 8.2.1 20180831 (GCC)

    opened by ilyalesokhin-starkware 8
  • Added iterator_tags for zip

    Added iterator_tags for zip

    High level: zip_iter should have the weakest iterator_tag over all of the iterators inside of it. So, if it's a zip of a bunch of vectors, we should be able to use zip_iter as a random_access_iterator

    Low level: There was lots of collateral damage implementing this. Important bits:

    A template metaprogram struct to figure out the weakest tag of a set of iterators (I put it in iter_base.hpp) Added a few missing methods for forward iterators Got rid of the std::iterator_traits specializations you did and instead put using declarations in the iterator classes (which it sounds like is the preferred approach) Added a few missing overloads for operator * (const and non const versions) in a bunch of the classes Addition of conditionally enabled operations for bidirectional and random access iterators to zip

    Here's the bad news. This is a breaking change because of the way the c++ stdlib/stl work. In order to sort a zipped range, I had to change the return type of operator * for zip_iter to a wrapped type, so I could add my own swap function, found by ADL. I'm pretty sure you CANNOT return the tuple by reference, and std::swap doesn't work for r-value tuples (which is dumb, since it contains references, see here: http://stackoverflow.com/questions/21588477/swap-temporary-tuples-of-references).

    To get almost-equiavelent behavior, I added a get function for my wrapped tuple type which can be found by ADL, so to fix broken code, you should just be able to say using std::get (for any real tuples you have), and replace all your std::get calls to get calls. There's an implicit conversion from my wrapper to std::tuple, but it doesn't work with template deduction, so it's basically useless.

    Emacs apparently hates your formatting and did its best to wreck it. If you know an easy way to fix it, I'm happy to fix everything and push another revision.

    If the breaking change is a dealbreaker, the rest of the stuff I added should still be useful, I think. I suspect there's other reason's you'd want to use higher-level iterators than sorting (although that's why I bothered implementing what I did).

    opened by benjones 6
  • Requesting rbegin and rend for iter::repeat

    Requesting rbegin and rend for iter::repeat

    Hi there,

    It would be better for me to have rbegin and rend for iter::repeat, so I can pass an iter::repeat object to iter::reversed, and it is trivial to implement.

    Thanks

    enhancement 
    opened by roastduck 5
  • Fix all `reference` type aliases to match the return type of operator* exactly.

    Fix all `reference` type aliases to match the return type of operator* exactly.

    Recent libc++ checks that an iterators reference type matches its operator*'s return type. This isn't the case across itertools and must be addressed. Testing with https://github.com/llvm/llvm-project/commit/36c746ca2d5b325a7ac64135c1ff8774c06ab34c

    opened by ryanhaining 0
  • product's repeat argument as a variant.

    product's repeat argument as a variant.

    I'd appreciate having the repeat argument accepted as a variable:

    std::string ss{"abcd"};
    int k{4};
    iter::product(ss,k);
    
    //instead of...
    
    std::string ss{"abcd"};
    iter::product<4>(ss); // has to be entered manually!
    
    opened by alphahmed 0
  • `product` with repeats as an argument

    `product` with repeats as an argument

    Hi, I came here from doing Project Euler in C++.

    I was googling for a library for doing product() and found this one, but it only allows a template argument.

    My use-case is to have seq be the digits {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, and repeats be the number of them. This is required for some problems.

    Having repeats would be great!

    opened by simonzack 5
  • request zip/zip_longest functionality

    request zip/zip_longest functionality

    Hi, good to see more Python like functions in c++.

    Is it possible to have zip and zip_longest use a single iterator over an arbitrary number of containers that are only known at runtime? Just like in Python when unpacking a list of lists? It would really be helpful for me.

    opened by ghost 2
Releases(v2.1)
  • v2.1(Jan 15, 2021)

  • v2.0(Dec 23, 2019)

    Final planned C++17 release.

    After this tag, enumerate will provide a pair<> instead of a custom type, so no more .index and .element. Users should use structured bindings as for (auto&& [i, e] : iter::enumerate(...)) or .first and .second.

    I will be working towards compatibility with ranges for C++20.

    Source code(tar.gz)
    Source code(zip)
  • v1.0(Mar 31, 2018)

  • v0.2.1(Apr 8, 2016)

    First C++14 release. Includes the addition of starmap() and various code reduction. All planned future work will be in C++14. (Replaces 0.2 which had some clang errors with starmap)

    Source code(tar.gz)
    Source code(zip)
  • v0.1(Sep 17, 2015)

    First tagged release of C++ itertools. All code uses C++11 (no C++14) and has been tested with gcc-4.8, gcc-5.2, and clang-3.7. gcc and clang work with libstdc++ and libc++ respectively. I will likely have one more C++11 release before master goes to C++14.

    Source code(tar.gz)
    Source code(zip)
An AI for playing NES Tetris at a high level. Based primarily on search & heuristic, with high quality board evaluation through value iteration.

StackRabbit An AI that plays NES Tetris at a high level. Primarily based on search & heuristic, with high-quality board eval through value iteration.

Greg Cannon 222 Sep 22, 2022
Libft is an individual project at 42 that requires us to re-create some standard C library functions including some additional ones that can be used later to build a library of useful functions for the rest of the program.

Libft is an individual project at 42 that requires us to re-create some standard C library functions including some additional ones that can be used later to build a library of useful functions for the rest of the program.

Paulo Rafael Ramalho 0 Apr 5, 2022
null 270 Sep 23, 2022
Python wrapper for Kaldi's native I/O. The internal implementation uses C++ code from Kaldi.

Python wrapper for Kaldi's native I/O. The internal implementation uses C++ code from Kaldi. A Python wrapper with pybind11 is provided to read ark/scp files from Kaldi in Python.

Fangjun Kuang 18 Sep 22, 2022
Itpp - IT++ library mirror/fork. C++ library of mathematical, signal processing and communication classes and functions.

Introduction ************ IT++ is a C++ library of mathematical, signal processing and communication classes and functions. Its main use is in simula

null 18 Apr 9, 2022
C++ wrappers for SIMD intrinsics and parallelized, optimized mathematical functions (SSE, AVX, NEON, AVX512)

C++ wrappers for SIMD intrinsics and parallelized, optimized mathematical functions (SSE, AVX, NEON, AVX512)

Xtensor Stack 1.5k Oct 2, 2022
Basic definitions and utility functions for GNSS raw measurement processing

gnss_comm Authors/Maintainers: CAO Shaozu (shaozu.cao AT gmail.com) The gnss_comm package contains basic definitions and utility functions for GNSS ra

HKUST Aerial Robotics Group 60 Sep 20, 2022
A commented version of my libft, with details about how my algorithms work and simple main functions to compile them.

LIBFT COMMENTED VERSION : ?? PART I : ?? FT_STRCHR : ?? PART II : ?? FT_SUBSTR : /* * DEFINITION : * CREATES A SUBSTRING FROM A STRING WITH PREDETER

Abdessamad Laamimi 1 Nov 11, 2021
Metamath - Meta mathematics. Symbolic functions and derivatives.

metamath Meta mathematic metamath is a tiny header-only library. It can be used for symbolic computations on single-variable functions, such as dynami

eg 29 Mar 3, 2022
📚 single header utf8 string functions for C and C++

?? utf8.h A simple one header solution to supporting utf8 strings in C and C++. Functions provided from the C header string.h but with a utf8* prefix

Neil Henning 1.3k Oct 1, 2022
⌨️ A 64-key keyboard with many layouts and functions

English | 简体中文 Report Bug · Request Feature A 64-key keyboard with many layouts and functions ✨ Keyboard Introduction Gazlowe is a 64-key keyboard wit

Seaton Jiang 4 Feb 13, 2022
Arduino library for basic aerial navigation functions used for processing Euler angles, direction cosine matrices, quaternions, frame conversions, and more.

navduino Arduino library for basic aerial navigation functions used for Euler angles Direction cosine matrices Quaternions Rodrigues Rotation Vectors

PB2 5 Jul 28, 2022
PikaScript is an ultra-lightweight Python engine with zero dependencies and zero-configuration, that can run with 4KB of RAM (such as STM32G030C8 and STM32F103C8), and is very easy to deploy and expand.

PikaScript 中文页| Star please~ 1. Abstract PikaScript is an ultra-lightweight Python engine with zero dependencies and zero-configuration, that can run

Lyon 858 Oct 5, 2022
Project to check which Nt/Zw functions your local EDR is hooking

Probatorum EDR Userland Hook Checker Probatorum will check which Nt/Zw functions your local EDR is hooking. Most credit for this code goes to SolomonS

null 150 Sep 15, 2022
Easily hook WIN32 x64 functions

About Library for easy hooking of arbitrary functions in WIN32 x64 executables. Only requires target function address. Disassembles the function prolo

tcpie 17 Jun 12, 2022
PoC tool to coerce Windows hosts to authenticate to other machines via MS-EFSRPC EfsRpcOpenFileRaw or other functions.

PetitPotam PoC tool to coerce Windows hosts to authenticate to other machines via MS-EFSRPC EfsRpcOpenFileRaw or other functions :) The tools use the

Topotam 1.3k Sep 23, 2022
header-only UTF-8 string functions based on STL-string

utf8_xxx header-only UTF-8 string functions based on STL-string size_t utf8_len(const std::string& _Str) std::string utf8_sub(const std::string& _Str,

Voidmatrix 2 Dec 27, 2021