I've got the following code that combines two vectors of arbitrary types into a combinatorial, i.e. std::vector<std::tuple<A, B>>
.
template<class A, class B>
std::vector<std::tuple<A, B>> combine(const std::vector<A>& a, const std::vector<B>& b) {
const auto combine_parts_ = [](const A& x, const B& y) {
auto result = std::tuple_cat(std::make_tuple(x), std::make_tuple(y));
return result;
};
std::vector<std::tuple<A, B>> results;
for (const auto& x : a) {
for (const auto& y : b) {
results.push_back(combine_parts_(x, y));
}
}
return results;
}
However, I'm unclear how to extend that to an arbitrary number of types/vectors. I don't care about duplicate types; in fact there may be two or more sets of the same type involved. That's okay.
Some example use cases, for instance:
const auto combinations = combine(
std::vector<int>({1,2,3})
, std::vector<int>({1,2,3})
);
const auto combinations2 = combine(
std::vector<int>({1,2,3})
, std::vector<int>({1,2,3})
, std::vector<bool>({true,false})
);
const auto combinations3 = combine(
std::vector<int>({1,2,3})
, std::vector<int>({1,2,3})
, std::vector<bool>({true,false})
, std::vector<char>({'a','b','c','d','e'})
);
Chiefly, what I want to do is avoid the ugly nested for loop. At the same time, I want to combine some unit testing combinatorial use cases in order to work with the resulting std::tuple<...>
as the test case.
Note, I am not talking about permutations of a homogeneous set here. That's been a point of confusion from prior questions.
I think it might have something to do with templates, variadics, std::tuple_cat
, somewhere along the way, but I don't know.
Thoughts? Suggestions?
The concatenation of vectors can be done by using combination function c. For example, if we have three vectors x, y, z then the concatenation of these vectors can be done as c(x,y,z). Also, we can concatenate different types of vectors at the same time using the same same function.
The easiest way to store multiple types in the same vector is to make them subtypes of a parent class, wrapping your desired types in classes if they aren't classes already.
Vectors allow you to store more than one value in a single data structure that puts all the values next to each other in memory. Vectors can only store values of the same type.
How to join two Vectors using STL in C++? Given two vectors, join these two vectors using STL in C++. Approach: Joining can be done with the help of set_union() function provided in STL.
If you want to compute the Cartesian product of heterogeneous vectors, you may do something like:
template <std::size_t N>
bool increase(const std::array<std::size_t, N>& sizes, std::array<std::size_t, N>& it)
{
for (std::size_t i = 0; i != N; ++i) {
const std::size_t index = N - 1 - i;
++it[index];
if (it[index] >= sizes[index]) {
it[index] = 0;
} else {
return true;
}
}
return false;
}
template <typename F, std::size_t ... Is, std::size_t N, typename Tuple>
void apply_impl(F&& f,
std::index_sequence<Is...>,
const std::array<std::size_t, N>& it,
const Tuple& tuple)
{
f(std::get<Is>(tuple)[it[Is]]...);
}
template <typename F, typename ... Ts>
void cartesian_product_apply(F&& f, const std::vector<Ts>&... vs)
{
constexpr std::size_t N = sizeof...(Ts);
std::array<std::size_t, N> sizes{{vs.size()...}};
std::array<std::size_t, N> it{};
do {
apply_impl(f, std::index_sequence_for<Ts...>(), it, std::tie(vs...));
} while (increase(sizes, it));
}
And finally:
template <typename ... Ts>
std::vector<std::tuple<Ts...>> cartesian_product(const std::vector<Ts>&... vs)
{
std::vector<std::tuple<Ts...>> res;
cartesian_product_apply([&res](const auto&... args) { res.emplace_back(args...); },
vs...);
return res;
}
With usage similar to:
std::vector<int> v1 = {1, 2, 3};
std::vector<std::string> v2 = {" A "," B "};
std::vector<int> v3 = {4, 5};
const auto res = cartesian_product(v1, v2, v3);
for (const auto& t : res) {
// ...
}
Demo
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With