Просто для удовольствия, вот более функциональный вариант решения в моем первом ответе:
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);
}
Я хочу иметь возможность передавать произвольную функцию в tabulate (), включая функции разной арности (т. е. f (x), f (x, y) и т. д.)
blockquote> [ 117] Сделатьtabulate
шаблон, который принимает объекты произвольных типов в качестве функций.
Я хочу создать функцию, которую я передаю «на лету», включая использование других функций (так же, как f создается из P и Q в первом фрагменте кода
blockquote> [ 119] Вы можете напрямую использовать лямбду в качестве параметра функции.
, если мне удастся передать такую функцию, как я могу запустить цикл для всех возможных аргументов f (т. Е. 0..q- 1 для каждого из аргументов) внутри tabulate ()?
blockquote>В псевдокоде:
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 << ' '; }