I've tried out implementing a combination generator using boost coroutines. It accepts an array of possible values and generates one array at a time for each combination. As a limitation, it only generates combinations of the same length as the array of possible values and does not check for duplicates inside the source array.
#include <boost/coroutine/coroutine.hpp>
#include <algorithm>
#include <iostream>
template<typename ItElements, typename ItArray, typename Fn>
void enumerate_combinations(ItElements elements_from, ItElements elements_to, ItArray array_from, ItArray array_to,
Fn&& callback)
{
for (auto it = elements_from; it != elements_to; ++it)
{
*array_from = *it;
if (std::distance(array_from, array_to) > 1)
{
enumerate_combinations(elements_from, elements_to, array_from + 1, array_to, callback);
}
else
{
callback();
}
}
}
template<typename T, size_t nr_elements>
auto enumerate_combinations(T(&elements)[nr_elements])
{
return boost::coroutines::asymmetric_coroutine<T(&)[nr_elements]>::pull_type(
[&](boost::coroutines::asymmetric_coroutine<T(&)[nr_elements]>::push_type& sink)
{
T buffer[nr_elements];
std::fill(std::begin(buffer), std::end(buffer), elements[0]);
enumerate_combinations(std::begin(elements), std::end(elements), std::begin(buffer), std::end(buffer),
[&]() {sink(buffer);});
});
}
template<typename T, size_t nr_elements>
std::ostream& print_array(T(&array)[nr_elements], std::ostream& stream, const char* separator = " ")
{
for (size_t i = 0; i < nr_elements - 1; i++)
{
stream << array[i];
if (separator)
{
stream << separator;
}
}
stream << array[nr_elements - 1];
return stream;
}
int main()
{
int digits[] = { 1, 2, 3 };
for (auto& combination : enumerate_combinations(digits))
{
print_array(combination, std::cout) << '\n';
}
}
Any thoughts or reviews?
Running the above code produces:
1 1 1
1 1 2
1 1 3
1 2 1
1 2 2
1 2 3
1 3 1
1 3 2
1 3 3
2 1 1
2 1 2
2 1 3
2 2 1
2 2 2
2 2 3
2 3 1
2 3 2
2 3 3
3 1 1
3 1 2
3 1 3
3 2 1
3 2 2
3 2 3
3 3 1
3 3 2
3 3 3