3 votes

Comment appliquer un tuple d'actions sur un tuple de nombres ?

J'ai deux tuples, l'un contenant des valeurs et l'autre contenant des actions pour ces valeurs. Maintenant, je veux appliquer l'action correspondante à chaque valeur, avec le moins de "surcharge" de code possible. Quelque chose comme l'exemple simplifié ci-dessous.

#include <iostream>
#include <boost/hana.hpp>

namespace hana = boost::hana;
using namespace hana::literals;

struct ThinkPositive
{
    void operator()(int &val) const
    {
        std::cout << "Think positive!\n";
        val = std::abs(val);
    }
};

struct Nice
{
    void operator()(int &val) const
    {
        std::cout << val << " is nice!\n";
    }
};

void numbers()
{
    auto handlers = hana::make_tuple(Nice{}, ThinkPositive{});
    auto nums = hana::make_tuple(5, -12);
    auto handlers_and_nums = hana::zip(handlers, nums);

    hana::for_each(handlers_and_nums, [](auto &handler_num) {
        handler_num[0_c](handler_num[1_c]);
    });

    auto result = hana::transform(handlers_and_nums, [](const auto &handler_num) {
        return handler_num[1_c];
    });

    hana::for_each(result, [](const auto num) {
        std::cout << "got " << num << '\n';
    });
}

int main()
{
    numbers();
}

Bien que l'exemple ci-dessus fonctionne, il serait plus agréable de modifier le contenu de nums en place.

Existe-t-il un moyen de modifier les chiffres en place ?

3voto

KrzaQ Points 1

Vous pourriez utiliser zip_with mais cela semble aller à l'encontre de sa nature (il faut que la fonction renvoie effectivement quelque chose, mais vos opérateurs () ne renvoie rien :

auto special_compose = [](auto&& l, auto&& r){ l(r); return 0; };
hana::zip_with(special_compose, handlers, nums);

Démonstration


Si vous pouvez faire en sorte que vos opérateurs retournent quelque chose, vous pouvez opter pour lockstep :

hana::fuse(hana::fuse(hana::lockstep(hana::always(0)))(handlers))(nums);

Démonstration

Il devrait y avoir quelque chose comme lockstep défini sans l'extérieur f mais je n'ai rien trouvé dans la documentation.


Une solution un peu plus standard (qui ne répondra pas à votre exigence d'une surcharge de code aussi faible que possible) :

template<typename Fs, typename Params, size_t... is>
void apply_in_lockstep_impl(Fs&& fs, Params&& ps, std::index_sequence<is...>){
    int x[] = { (fs[hana::integral_c<size_t,is>](ps[hana::integral_c<size_t,is>]),0)... };
}

template<typename Fs, typename Params>
void apply_in_lockstep(Fs&& fs, Params&& ps){
    static_assert(hana::size(fs) == hana::size(ps), "");
    apply_in_lockstep_impl(std::forward<Fs>(fs),
                           std::forward<Params>(ps),
                           std::make_index_sequence<decltype(hana::size(ps))::value>{});
}

mais sur le site d'appel, c'est plus joli :

apply_in_lockstep(handlers, nums);

Démonstration


Comme indiqué dans les commentaires, un autre niveau d'indirection peut également être utile. Ici, il s'agirait de transformer la séquence en une séquence de pointeurs, par l'intermédiaire desquels les valeurs originales sont modifiées.

auto nums_ptr = hana::transform(nums, [](auto &num) { return &num; });
auto handlers_and_nums = hana::zip(handlers, nums_ptr);
hana::for_each(handlers_and_nums, [](auto &handler_num) {
    handler_num[0_c](*handler_num[1_c]);
});

Démonstration


Une autre méthode, plus "traditionnelle", consiste à itérer sur une plage. Cela revient à utiliser une vieille boucle for.

auto indices = hana::make_range(0_c, hana::length(handlers));
hana::for_each(indices, [&](auto i) {
    handlers[i](nums[i]);
});

Démonstration

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X