Skip to content
forked from cgsdfc/itertools

Python's iterators for modern C++.

License

Notifications You must be signed in to change notification settings

c2akula/itertools

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

81 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Build Status Build status

Itertools

Python's builtin iterators & itertools in modern C++. Get back all the goodness you miss in Python when typing C++ code! Just use it and you're be fine.

Features

  • Header-only.
  • Lazy sequence with very little overhead.
  • Very intuitive syntax and semantic.
  • All familiar to Python programmers.
  • Simple and straightforward implementation.
  • Work with both rvalue and lvalue references.
  • Iterables can be composed arbitrarily.

Examples

Use enumerate to loop through a container with a counting index. You are no longer forced to abandon range-based loop when an index is needed! Instead of

std::vector<int> v;
for (int i = 0; i < v.size(); i++) {
    printf("The #%d item is %d\n", i, v[i]);
}

You write

std::vector<int> v;
for (auto [i, val] : enumerate(v)) {
    printf("The #%d item is %d\n", i, val);
}

Use zip to simultaneously loop over several containers, stopping at the shortest one amongst them. Again, you avoid getting your hand dirty to roll out an index-based for and stay the safe zone of range-based for.

std::vector<std::string> first_names, last_names;
std::list<unsigned> phone_numbers;

for (auto [first, last, phone] : zip(first_names, last_names, phone_numbers)) {
    printf("Person %s %s, phone number is %u\n", first, last, phone);
}

Just looks great.

When you need to apply some function to a sequence and consume the result iteratively, std::transform sucks you up by forcing you to back the output iterator with some container. In fact std::transform is designed to resemble std::copy and its friends to consume iterators but not to become an iterable itself. Suppose you want to turn a list of strings upper case, see this example.

std::vector<std::string> strings{"hello", "world"};
std::vector<std::string> upper_strings;

// You have to allocate an output variable and use the ugly back_inserter.
std::transform(std::begin(strings), std::end(strings), std::back_inserter(upper_strings), ::toupper);

// Another use case: write to a stream using the adapter ostream_iterator.
std::ostringstream os;
std::transform(std::begin(strings), std::end(strings), std::ostream_iterator<std::string>(os, ", "), ::toupper);

In either case, you have to first save the results in some place and then process them. It is a waste of time and space if you just want to process the result iteratively.

std::vector<std::string> strings{"hello", "world"};
for (auto &&str : map(::upper, strings)) {
    std::cout << str << ' ';
}

This just looks much cleaner and indeed more efficient.

With map and reduce you can rule the world.

Your last resistance to the index-based loop will be the range class, which implements the index-based loop but provide you the range-based interface. Think about saying goodbye to the plain old index loop!

std::vector<int> v{1, 2, 3, 4};
for (auto i : range(0, v.size()) {
    printf("%d\n", v[i]);
}

// reverse
for (auto i : range(v.size() - 1, -1, -1)) {
    printf("%d\n", v[i]);
}

Install

cd itertools && mkdir build && cd build
make && make install

License

MIT License

About

Python's iterators for modern C++.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • C++ 88.4%
  • CMake 4.2%
  • Batchfile 3.3%
  • PowerShell 2.1%
  • C 1.4%
  • Python 0.6%