c++ - subconjunto - TMP: ¿cómo generalizar un producto cartesiano de vectores?
relacion en producto cartesiano (2)
Existe una excelente solución de C ++ (en realidad, 2 soluciones: recursiva y no recursiva), en un producto cartesiano de un vector de vectores enteros . Para fines de ilustración / simplicidad, centrémonos en la versión no recursiva .
Mi pregunta es, ¿cómo se puede generalizar este código con plantillas para tomar una std::tuple
de vectores homogéneos que se ve así:
{{2,5,9},{"foo","bar"}}
y generar un vector homogéneo de tuple
{{2,"foo"},{2,"bar"},{5,"foo"},{5,"bar"},{9,"foo"},{9,"bar"}}
Si hace la vida más fácil, supongamos que los vectores internos en la entrada son homogéneos. Por lo tanto, no se permiten entradas como esta: {{5,"baz"}{''c'',-2}}
EDIT cambió la entrada del vector irregular a una tupla
Ha pasado un tiempo desde que he estado haciendo esto, pero aquí hay un primer intento. Sin duda se puede mejorar.
template<unsigned fixedIndex, class T>
class DynamicTupleGetter
{
typedef typename std::tuple_element<fixedIndex, T>::type RetType;
public:
static RetType get(unsigned dynIndex, const T& tupleInstance)
{
const RetType& ret = std::get<fixedIndex>(tupleInstance);
if (fixedIndex == dynIndex)
return ret;
return DynamicTupleGetter<fixedIndex - 1, T>::get(dynIndex, tupleInstance);
}
};
template<class T>
class DynamicTupleGetter<0, T>
{
typedef typename std::tuple_element<0, T>::type RetType;
public:
static RetType get(unsigned dynIndex, const T& tupleInstance)
{
assert(dynIndex == 0);
return std::get<0>(tupleInstance);
}
};
template<class Source>
struct Converter
{
typedef typename std::tuple_element<0, Source>::type Zeroth;
typedef typename std::tuple_element<1, Source>::type First;
static const size_t size0 = std::tuple_size<Zeroth>::value;
static const size_t size1 = std::tuple_size<First>::value;
static const size_t outerProductSize = size0 * size1;
typedef typename std::tuple_element<0, Zeroth>::type BaseType0;
typedef typename std::tuple_element<0, First>::type BaseType1;
typedef typename std::tuple<BaseType0, BaseType1> EntryType;
typedef std::array<EntryType, outerProductSize> DestinationType;
DestinationType create(const Source& source)
{
Zeroth zeroth = std::get<0>(source);
First first = std::get<1>(source);
typedef typename DynamicTupleGetter<size0 -1, Zeroth> ZerothGetter;
typedef typename DynamicTupleGetter<size1 -1, First> FirstGetter;
DestinationType result;
size_t resultIndex = 0;
for(size_t i = 0; i < size0; ++i)
for(size_t j = 0; j < size1; ++j)
{
std::get<0>(result[resultIndex]) = ZerothGetter::get(i, zeroth) ;
std::get<1>(result[resultIndex]) = FirstGetter::get(j, first);
++resultIndex;
}
return result;
}
};
template<class T>
void create(const T& source)
{
Converter<T> converter;
Converter<T>::DestinationType result = converter.create(source);
std::cout << std::get<0>(std::get<3>(result)) << "," << std::get<1>(std::get<3>(result)) << std::endl;
}
auto intPart = std::make_tuple(2,5,9);
auto stringPart = std::make_tuple("foo","bar");
auto source = std::make_tuple(intPart, stringPart);
void f()
{
create(source);
}
Solución recursiva más simple. Toma vectores como argumentos de función, no como una tupla. Esta versión no construye tuplas temporales, sino que usa lambdas en su lugar. Ahora no hace copias / movimientos innecesarios y parece optimizarse con éxito.
#include<tuple>
#include<vector>
// cross_imp(f, v...) means "do `f` for each element of cartesian product of v..."
template<typename F>
inline void cross_imp(F f) {
f();
}
template<typename F, typename H, typename... Ts>
inline void cross_imp(F f, std::vector<H> const& h,
std::vector<Ts> const&... t) {
for(H const& he: h)
cross_imp([&](Ts const&... ts){
f(he, ts...);
}, t...);
}
template<typename... Ts>
std::vector<std::tuple<Ts...>> cross(std::vector<Ts> const&... in) {
std::vector<std::tuple<Ts...>> res;
cross_imp([&](Ts const&... ts){
res.emplace_back(ts...);
}, in...);
return res;
}
#include<iostream>
int main() {
std::vector<int> is = {2,5,9};
std::vector<char const*> cps = {"foo","bar"};
std::vector<double> ds = {1.5, 3.14, 2.71};
auto res = cross(is, cps, ds);
for(auto& a: res) {
std::cout << ''{'' << std::get<0>(a) << '','' <<
std::get<1>(a) << '','' <<
std::get<2>(a) << "}/n";
}
}