Лямбда / функция произвольной арности и с захватами в качестве аргумента функции

Просто для удовольствия, вот более функциональный вариант решения в моем первом ответе:

function cartesian() {
    var r = [], args = Array.from(arguments);
    args.reduceRight(function(cont, factor, i) {
        return function(arr) {
            for (var j=0, l=factor.length; j<l; j++) {
                var a = arr.slice(); // clone arr
                a[i] = factor[j];
                cont(a);
            }
        };
    }, Array.prototype.push.bind(r))(new Array(args.length));
    return r;
}

Альтернативный, для полной скорости мы можем динамически скомпилировать наши собственные петли:

function cartesian() {
    return (cartesian.cache[arguments.length] || cartesian.compile(arguments.length)).apply(null, arguments);
}
cartesian.cache = [];
cartesian.compile = function compile(n) {
    var args = [],
        indent = "",
        up = "",
        down = "";
    for (var i=0; i<n; i++) {
        var arr = "$"+String.fromCharCode(97+i),
            ind = String.fromCharCode(105+i);
        args.push(arr);
        up += indent+"for (var "+ind+"=0, l"+arr+"="+arr+".length; "+ind+"<l"+arr+"; "+ind+"++) {\n";
        down = indent+"}\n"+down;
        indent += "  ";
        up += indent+"arr["+i+"] = "+arr+"["+ind+"];\n";
    }
    var body = "var res=[],\n    arr=[];\n"+up+indent+"res.push(arr.slice());\n"+down+"return res;";
    return cartesian.cache[n] = new Function(args, body);
}
2
задан Yauhen Yakimenka 24 February 2019 в 11:46
поделиться

1 ответ

Я хочу иметь возможность передавать произвольную функцию в tabulate (), включая функции разной арности (т. е. f (x), f (x, y) и т. д.)

[ 117] Сделать tabulate шаблон, который принимает объекты произвольных типов в качестве функций.

Я хочу создать функцию, которую я передаю «на лету», включая использование других функций (так же, как f создается из P и Q в первом фрагменте кода

[ 119] Вы можете напрямую использовать лямбду в качестве параметра функции.

, если мне удастся передать такую ​​функцию, как я могу запустить цикл для всех возможных аргументов f (т. Е. 0..q- 1 для каждого из аргументов) внутри tabulate ()?

В псевдокоде:

params = {0, ..., 0};

while (1)
{
    // Call function with `params` here.

    int i = 0;
    for (i = 0; i < params.size(); i++)
    {
        params[i]++;
        if (params[i] == q)
            params[i] = 0;
        else
            break;
    }
    if (i == params.size())
        break;
}

На практике вам нужно хранить параметры в std::array (или std::tuple ], как показано в приведенном ниже коде), и используйте std::apply для вызова вашей функции с этими параметрами.


Полная реализация:

#include <cstddef>
#include <iostream>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>

template <typename T, typename ...P, std::size_t ...I>
bool increment_tuple_impl(T q, std::tuple<P...> &t, std::index_sequence<I...>)
{
    auto lambda = [&](auto index) -> bool
    {
        auto &elem = std::get<index.value>(t);
        elem++;
        if (elem == q)
        {
            elem = 0;
            return 0;
        }
        else
        {
            return 1;
        }
    };

    return (lambda(std::integral_constant<std::size_t, I>{}) || ...);
}

template <typename T, typename ...P>
bool increment_tuple(T q, std::tuple<P...> &t)
{
    return increment_tuple_impl(q, t, std::make_index_sequence<sizeof...(P)>{});
}

template <typename T, typename F, std::size_t MaxArity, typename ...P>
auto tabulate_impl(T q, F &&f)
{
    if constexpr (!std::is_invocable_v<F, P...>)
    {
        static_assert(sizeof...(P) < MaxArity, "Invalid function.");
        return tabulate_impl<T, F, MaxArity, P..., T>(q, std::forward<F>(f));
    }
    else
    {
        using return_type = std::invoke_result_t<F, P...>;
        std::vector<return_type> vec;
        std::tuple<P...> params{};
        do
        {
            vec.push_back(std::apply(f, params));
        }
        while (increment_tuple(q, params));
        return vec;
    }
}

template <typename T, typename F>
auto tabulate(T q, F &&f)
{
    constexpr int max_arity = 8;
    return tabulate_impl<T, F, max_arity, T>(q, std::forward<F>(f));
}

int main()
{
    auto v = tabulate(3, [](int x, int y){return x*10 + y;});

    // Prints `0 10 20 1 11 21 2 12 22`.
    for (auto x : v)
        std::cout << x << ' ';
}
0
ответ дан HolyBlackCat 24 February 2019 в 11:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: