Lambda/function 任意数量且捕获作为函数参数
Lambda/function of arbitrary arity and with captures as a function argument
在我的 C++ 程序中,我经常需要在某个小的有限域上根据函数参数的所有可能值构建一个函数值向量。例如,像这样:
int q = 7;
vector<int> GFq;
for (int x = 0; x < q; x++) GFq.push_back(x);
auto P = [q](int x, int y) -> int { return (x*x+y) % q; };
auto Q = [q](int x, int y) -> int { return (x+2*y) % q; };
auto f = [q,P,Q](int x1, int y1, int x2, int y2)
-> int {return (P(x1,y1) + Q(x2,y2)) % q; }
vector<int> table;
for (int x1: GFq) for (int y1: GFq) for (int x2: GFq) for (int y2: GFq)
table.push_back(f(x1,y1,x2,y2));
这种模式在我的代码中经常重复出现,所以我很自然地想将它变成一个函数。所以我需要这样的东西:
template<typename F> // not sure if I need to use templates
vector<int> tabulate(int q, F f) {
// run through values 0..q-1 for all arguments of f
// and store the values of f to the resulting vector
}
一些questions/issues:
- 我希望能够将任意函数传递给
tabulate()
,包括那些不同数量的函数(即 f(x)
、f(x,y)
等)
- 我想构造我传递给 "on the fly" 的函数,包括其他函数的使用(与
f
相同的方式是从 P
和 Q
构造第一个代码片段
- 如果我设法传递了这样一个函数,我怎样才能 运行 在
f
的所有可能参数上循环(即每个参数的 0..q-1
) 13=]?
I want to be able to pass an arbitrary function to tabulate() including those of different arity (i.e. f(x), f(x,y), etc.)
制作 tabulate
接受任意类型对象作为函数的模板。
I want to construct the function I pass "on the fly", including usage of other functions (the same way as f is constructed from P and Q in the first code snippet
您可以直接使用 lambda 作为函数参数。
if I manage to pass such a function, how can I run a loop over all possible arguments of f (i.e. 0..q-1 for each of its arguments) inside 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 << ' ';
}
在我的 C++ 程序中,我经常需要在某个小的有限域上根据函数参数的所有可能值构建一个函数值向量。例如,像这样:
int q = 7;
vector<int> GFq;
for (int x = 0; x < q; x++) GFq.push_back(x);
auto P = [q](int x, int y) -> int { return (x*x+y) % q; };
auto Q = [q](int x, int y) -> int { return (x+2*y) % q; };
auto f = [q,P,Q](int x1, int y1, int x2, int y2)
-> int {return (P(x1,y1) + Q(x2,y2)) % q; }
vector<int> table;
for (int x1: GFq) for (int y1: GFq) for (int x2: GFq) for (int y2: GFq)
table.push_back(f(x1,y1,x2,y2));
这种模式在我的代码中经常重复出现,所以我很自然地想将它变成一个函数。所以我需要这样的东西:
template<typename F> // not sure if I need to use templates
vector<int> tabulate(int q, F f) {
// run through values 0..q-1 for all arguments of f
// and store the values of f to the resulting vector
}
一些questions/issues:
- 我希望能够将任意函数传递给
tabulate()
,包括那些不同数量的函数(即f(x)
、f(x,y)
等) - 我想构造我传递给 "on the fly" 的函数,包括其他函数的使用(与
f
相同的方式是从P
和Q
构造第一个代码片段 - 如果我设法传递了这样一个函数,我怎样才能 运行 在
f
的所有可能参数上循环(即每个参数的0..q-1
) 13=]?
I want to be able to pass an arbitrary function to tabulate() including those of different arity (i.e. f(x), f(x,y), etc.)
制作 tabulate
接受任意类型对象作为函数的模板。
I want to construct the function I pass "on the fly", including usage of other functions (the same way as f is constructed from P and Q in the first code snippet
您可以直接使用 lambda 作为函数参数。
if I manage to pass such a function, how can I run a loop over all possible arguments of f (i.e. 0..q-1 for each of its arguments) inside 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 << ' ';
}