Как перебрать два STL-подобных контейнера (декартово произведение)

Я хотел бы уменьшить следующее с BOOST

typedef std::vector<int>::const_iterator Iterator;

for(Iterator i = v1.begin(), ie = v1.end(); i != ie; ++i) {
for(Iterator j = v2.begin(), je = v2.end(); j != je; ++j) {
doSomething( *i, *j );
}
}

Я имею в виду инкапсулировать 2 цикла в одну конструкцию (с Boost.Foreach, Boost.Range, Boost.Iterator и т. Д.). Следующие лайки, которые я хотел бы видеть (это просто идея, а не то, что я хочу видеть)

BOOST_FOREACH(boost::tuple<int,int> &p, ..._range(v1, v2)) {
doSomething(p.get<0>(), p.get<1>());
}

Как это можно сделать?

РЕДАКТИРОВАТЬ: Кстати, в Python вы можете написать просто

for (x1,x2,x3) in (l1,l2,l3):
print "do something with", x1, x2, x3

1

Решение

Вы можете использовать различные шаблоны для создания декартового произведения. Код ниже находится на @zch отличный ответ на другой вопрос.

#include <tuple>                        // make_tuple, tuple
#include <utility>                      // pair
#include <vector>                       // vector

namespace detail {

// the lambda is fully bound with one element from each of the ranges
template<class Op>
void insert_tuples(Op op)
{
// evaluating the lambda will insert the currently bound tuple
op();
}

// "peal off" the first range from the remaining tuple of ranges
template<class Op, class InputIterator1, class... InputIterator2>
void insert_tuples(Op op, std::pair<InputIterator1, InputIterator1> head, std::pair<InputIterator2, InputIterator2>... tail)
{
// "peal off" the elements from the first of the remaining ranges
// NOTE: the recursion will effectively generate the multiple nested for-loops
for (auto it = head.first; it != head.second; ++it) {
// bind the first free variable in the lambda, and
// keep one free variable for each of the remaining ranges
detail::insert_tuples(
[=](InputIterator2... elems) mutable { op(it, elems...); },
tail...
);
}
}

}       // namespace detail

// convert a tuple of ranges to the range of tuples representing the Cartesian product
template<class OutputIterator, class... InputIterator>
void cartesian_product(OutputIterator result, std::pair<InputIterator, InputIterator>... dimensions)
{
detail::insert_tuples(
[=](InputIterator... elems) mutable { *result++ = std::make_tuple(*elems...); },
dimensions...
);
}

Вы можете назвать это так:

 int main()
{
bool b[] = { false, true };
int i[] = { 0, 1 };
std::string s[] = { "Hello", "World" };

std::vector< std::tuple<bool, int, std::string> > cp = {
std::make_tuple(false, 0, "Hello") ,
std::make_tuple(false, 0, "World"),
std::make_tuple(false, 1, "Hello"),
std::make_tuple(false, 1, "World"),
std::make_tuple(true,  0, "Hello"),
std::make_tuple(true,  0, "World"),
std::make_tuple(true,  1, "Hello"),
std::make_tuple(true,  1, "World")
};

std::vector< std::tuple<bool, int, std::string> > result;
cartesian_product(
std::back_inserter(result),
std::make_pair(std::begin(b), std::end(b)),
std::make_pair(std::begin(i), std::end(i)),
std::make_pair(std::begin(s), std::end(s))
);

std::cout << std::boolalpha << (result==cp) << "\n";

// now use a single flat loop over result to do your own thing
for (auto t: result) {
std::cout << std::get<0>(t) << ", ";
std::cout << std::get<1>(t) << ", ";
std::cout << std::get<2>(t) << "\n";
}
}

онлайн выход.

Я не очень знаком с Boost.Range, но вы можете легко адаптировать пару итераторов для использования диапазонов Boost. Одним из недостатков является то, что он не будет инкрементным, и вам придется сгенерировать весь декартовой продукт заранее, прежде чем вы сможете его итерировать (код в вашем вопросе, похоже, не нуждается в break хоть).

3

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector