我想计算函数组合 - f ( g (param) )。这是我尝试过的:
auto fComposition(auto&& f, auto&& g, auto&&... params)
{
/* some stuff */
auto result = std::forward<decltype(f)>(f)(
std::forward<decltype(g)>(g)(
std::forward<decltype(params)>(param)
)
);
/* other stuff */
return result;
};
Run Code Online (Sandbox Code Playgroud)
编译用
g++ -std=c++17 src.cpp
Run Code Online (Sandbox Code Playgroud)
基本测试
#include <random>
#include <math.h>
int main()
{
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<double> distr(-1.0, 1.0);
auto xx = fComposition(round, distr, gen);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我收到消息说它无法识别第一个函数的类型。
顺便说一句,这真的是你的代码吗?您没有扩展,params
所以它不应该编译。
I. 定义组合的方式,它与简单的调用没有区别:除了额外的字符输入之外,您的fComposition(f, g, arg)
组合是相同的。f(g(arg))
真正的组合通常是一个组合器,它接受两个函数并返回一个闭包,当在实际参数上调用时,连续应用它们。就像是:
template<class F, class G> auto comp(F f, G g) {
return [f, g](auto &&... args) {
return f(g(std::forward<decltype(args)>(args)...));
};
}
Run Code Online (Sandbox Code Playgroud)
(注意按值绑定。在 C++17 中,它们比二十年前更先进。:) 您可以根据喜好添加std::move
s 和std::forward
s。)
这样你就可以编写两个函数:
auto fg = comp(f, g);
Run Code Online (Sandbox Code Playgroud)
然后调用参数的结果:
auto x = fg(arg1, arg2);
Run Code Online (Sandbox Code Playgroud)
二. 但实际上,为什么要限制我们使用两个操作数呢?在 Haskell 中,(.)
是一个单一的二元函数。在 C++ 中,我们可以拥有一整棵重载树:
template<class Root, class... Branches> auto comp(Root &&root, Branches &&... branches) {
return [root, branches...](auto &&...args) {
return root(branches(std::forward<decltype(args)>(args)...)...);
};
}
Run Code Online (Sandbox Code Playgroud)
现在您可以将任何 AST 封装在单个可调用中:
int f(int x, int y) { return x + y; }
int g(int x) { return x * 19; }
int h(int x) { return x + 2; }
#include <iostream>
int main() {
auto fgh = comp(f, g, h);
std::cout << fgh(2) << '\n';
}
Run Code Online (Sandbox Code Playgroud)
据我所知,类似的技术是在 11 标准之前的 C++ 中实现匿名闭包的唯一方法。
三.但是等等,有图书馆解决方案吗?事实上,是的。来自std::bind
的描述
如果存储的参数 arg 的类型为 T
std::is_bind_expression<T>::value == true
(例如,另一个绑定表达式直接传递到对绑定的初始调用中),则绑定执行函数组合:而不是传递绑定子表达式将返回的函数对象,而是传递子表达式被急切地调用,并且其返回值被传递给外部可调用对象。如果绑定子表达式有任何占位符参数,它们将与外部绑定共享(从 中挑选u1, u2, ...
)。具体来说,上面调用vn
中的参数是,同一调用中的类型是(cv 限定与 g 的限定相同)。std::invoke
arg(std::forward<Uj>(uj)...)
Vn
std::result_of_t<T cv &(Uj&&...)>&&
抱歉,目前没有示例。>_<
PS 是的,std::round
是一个重载函数,因此您应该对其进行类型转换以指定您需要组成哪个重载。
归档时间: |
|
查看次数: |
1571 次 |
最近记录: |