При наличии кортежа с произвольным числом векторов, содержащих разные типы, как извлечь вектор с минимальным размером? - PullRequest
0 голосов
/ 17 декабря 2018

В моем приложении я генерирую кортеж со множеством векторов, содержащих значения.Я хотел бы иметь общий способ итерации по кортежу для извлечения одного вектора с наименьшими значениями.

Например:

auto t = std::make_tuple(std::vector<int>({1,2}), std::vector<double>({1.0, 2.0, 3.0}));

Как извлечь вектор, который содержит наименьшийзначения?

Ответы [ 2 ]

0 голосов
/ 17 декабря 2018

Давайте предположим, что у нас нет дубликатов в нашей пачке типов, или способ их удаления , а также C ++ 11 backport из std::index_sequence

#include <map>
#include <tuple>
#include <functional>
#include <iostream>

#include <boost/variant>
#include <future/index_sequence>

namespace detail {
    template<typename Variant, typename Tuple, std::size_t... Is>
    std::map<std::size_t, std::function<Variant(const Tuple &)>> from_tuple_map(index_sequence<Is...>)
    {
        return { { Is, [](const Tuple & tup){ return std::get<Is>(tup); } }... };
    }

    struct GetSize
    {
        template<typename T>
        std::size_t operator()(const std::vector<T> & vec){ return vec.size(); }
    }; // becomes C++14 generic lambda
}

template<typename... Ts>
boost::variant<Ts...> from_tuple(const std::tuple<Ts...> & tup)
{
    auto map = detail::from_tuple_map<boost::variant<Ts...>, std::tuple<Ts...>>(make_index_sequence<sizeof...(Ts)>());
    auto get_size = GetSize{};
    std::size_t best = 0;
    std::size_t len = visit(get_size, map[best](tup));
    for (std::size_t trial = 1; trial < sizeof...(Ts); ++trial)
    {
        std::size_t trial_len = visit(get_size, map[trial](tup));
        if (trial_len > len)
        {
            best = trial;
            len = trial_len;
        }
    }
    return map[best](tup);
}

int main()
{
    auto x = from_tuple(std::make_tuple(std::vector<int>({1,2}), std::vector<double>({1.0, 2.0, 3.0})));
    visit([](const auto & a){ std::cout << a[1]; }, x);
}

Посмотреть вживую (с помощью компилятора C ++ 17)

0 голосов
/ 17 декабря 2018

При наличии кортежа с векторами, содержащими различных типов как извлечь вектор с минимальным размером?

Вы не можете Выне может напрямую.

Поскольку они являются разными типами, решение основывается на значениях (не на типах), поэтому вы не можете определить тип извлеченного времени компиляции (std::tuple не может быть constexpr), а C ++ является языком со строгой типизацией.

best простейшая вещь, которую вы можете сделать, это извлечь index вектора с минимальным размером.Потому что в этом случае извлеченное значение является целым числом (std::size_t, например), и вы можете перебирать векторы в кортеже, чтобы выбрать тот с меньшим количеством элементов.

Отличается, если вам нужно извлечьstd::array с минимальным размером

auto t = std::make_tuple(std::array<int, 2u>({1,2}),
                         std::array<double, 3u>({1.0, 2.0, 3.0}));

, поскольку размер (2u для первого, 3u для второго) является известным временем компиляции, поэтому вы можете выбрать время компиляции второго массива.

Если вы можете использовать C ++ 17, вы можете использовать std::variant, то есть тип, который может содержать все типы вашего кортежа.

Как указывает Caleth (спасибо), если вы можете использовать толькоC ++ 11 / C ++ 14 с расширенными библиотеками, вы можете использовать boost::variant.Но я не знаю этого, поэтому я не могу показать вам конкретный пример (но вы можете увидеть ответ Калета)

Ниже приведен пример с кортежем двух типов

template <typename T1, typename T2>
std::variant<T1, T2> getLess (std::tuple<T1, T2> const & tp)
 {
   std::variant<T1, T2>  v;

   if ( std::get<0>(tp).size() < std::get<1>(tp).size() )
       v = std::get<0>(tp);
   else
       v = std::get<1>(tp);

   return v;
 }

int main ()
 {
   std::vector<int>     vi {1, 2, 3};
   std::vector<double>  vd {1.0, 2.0};

   auto gl = getLess(std::make_tuple(vi, vd));
 }

Это работает с кортежем двух типов.Но с кортежем N-типов (с высотой N) становится сложным, потому что вы не можете записать что-то как

auto min_length = std::get<0>(tp).size();
auto min_index  = 0u;

for ( auto ui = 1u ; ui < N ; ++ui )
   if ( std::get<ui>(tp).size() < min_length )
    {
       min_length = std::get<ui>(tp).size();
       min_index  = ui;
    }

, потому что вы не можете передать std::get<>() значение времени выполнения как ui.

Та же проблема при назначении варианта.Вы не можете просто написать

v = std::get<min_index>(tp);

, потому что min_index является значением времени выполнения.

Вы должны пройти через switch()

switch ( min_length )
 {
   case 0: v = std::get<0>(tp); break;
   case 1: v = std::get<1>(tp); break;
   case 2: v = std::get<2>(tp); break;
   case 3: v = std::get<3>(tp); break;
   case 4: v = std::get<4>(tp); break;

   // ...

   case N-1: v = std::get<N-1>(tp); break;
 };

или что-то в этом роде.

Как вы видите, все сложнее и сложнее, если вы хотите, чтобы функция getLess() была переменной.

Для случая с вариадой лучшее, что я могу себе представить(но это решение C ++ 17; см. ответ Калет для решения C ++ 11) заключается в использовании вспомогательной функции и свертывания шаблонов следующим образом.

#include <tuple>
#include <vector>
#include <variant>
#include <iostream>

template <typename ... Ts, std::size_t ... Is>
auto getLessHelper (std::tuple<Ts...> const & tp,
                    std::index_sequence<0, Is...> const &)
 {
   std::variant<Ts...> var_ret  { std::get<0>(tp) };
   std::size_t         min_size { std::get<0>(tp).size() };

   ((std::get<Is>(tp).size() < min_size ? (var_ret = std::get<Is>(tp),
                                           min_size = std::get<Is>(tp).size())
                                        : 0u), ...);

   return var_ret;
 }

template <typename ... Ts>
auto getLess (std::tuple<Ts...> const & tp)
 { return getLessHelper(tp, std::index_sequence_for<Ts...>{}); }

int main ()
 {
   std::vector<int>     vi {1, 2, 3};
   std::vector<double>  vd {1.0, 2.0};
   std::vector<float>   vf {1.0f};

   auto gl = getLess(std::make_tuple(vi, vd, vf));

   std::cout << std::visit([](auto const & v){ return v.size(); }, gl)
       << std::endl; // print 1, the size() of vf
 }
...