如何在c ++ 17中定义函数组成? [英] How to define the function composition in c++17?

查看:79
本文介绍了如何在c ++ 17中定义函数组成?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想计算函数的组成-f(g(param)).这是我尝试过的:

I would like to compute the function composition -- f ( g (param) ). Here is what I tried:

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;
};

使用

g++ -std=c++17 src.cpp

基本测试

#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;
}

我收到一条消息,它无法识别第一个函数的类型.

I've got the message that it doesn't recognize the type of first function .

推荐答案

顺便说一句,这真的是您的代码吗?您没有在扩展 params ,所以它不应该编译.

BTW, is this really your code? You're not expanding params so it should not compile.

I.定义构图的方式与简单的调用是无法区分的:您的 fComposition(f,g,arg) f(g(arg))相同,除了输入多余的字符.真正的组合物通常是一个接受两个函数并返回一个闭包的组合器,当对实际参数调用该闭包时,它们会连续应用它们.像这样:

I. The way you define composition, it is indistinguishable from a simple invocation: your fComposition(f, g, arg) is the same as f(g(arg)) except for extra characters typing. The real composition is usually a combinator that accepts two functions and returns a closure that, when invoked on actual arguments, applies them in succession. Something like:

template<class F, class G> auto comp(F f, G g) {
    return [f, g](auto &&... args) {
        return f(g(std::forward<decltype(args)>(args)...));
    };
}

(请注意按值绑定.在C ++ 17中,它们比二十年前更先进.:)您可以添加 std :: move s和 std :: forward s(按口味).

(Note by-values bindings. In C++17, they are more advanced than twenty years ago. :) You can add std::moves and std::forwards by taste.)

通过这种方式,您可以组成两个函数:

This way you compose two functions:

auto fg = comp(f, g);

,然后在参数上调用结果:

and later invoke the result on arguments:

auto x = fg(arg1, arg2);

II.但是实际上,为什么限制自己有两个操作数?在Haskell中,(.)是单个二进制函数.在C ++中,我们可以有一整个重载树:

II. But really, why limit ourselves with two operands? In Haskell, (.) is a single binary function. In C++, we can have a whole tree of overloads:

template<class Root, class... Branches> auto comp(Root &&root, Branches &&... branches) {
     return [root, branches...](auto &&...args) {
         return root(branches(std::forward<decltype(args)>(args)...)...);
     };
}

现在您可以将任何AST封装在一个可调用的容器中:

Now you can encapsulate any AST in a single callable:

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';
}

类似的技术是我知道的在11标准之前的C ++中具有匿名闭包的唯一方法.

A similar technique was the only way known to me to have anonymous closures in C++ prior to 11 standard.

III.但是,等等,有图书馆解决方案吗?实际上,是的.来自 std :: bind 的描述

III. But wait, is there a library solution? In fact, yes. From std::bind's description

如果存储的参数arg的类型为T,且其 std :: is_bind_expression< T> :: value == true (例如,另一个绑定表达式直接传递到了初始绑定中)),然后bind执行功能组合:不传递绑定子表达式将返回的功能对象,而是急切地调用该子表达式,并将其返回值传递给外部可调用对象.如果bind子表达式具有任何占位符参数,则它们与外部绑定共享(从 u1,u2,... 中挑选).具体来说,上述 std :: invoke 调用中的参数 vn arg(std :: forward< Uj>(uj)...),并且同一调用中的类型 Vn std :: result_of_t< T cv&(Uj& ...)>& (cv资格与g)相同.

If the stored argument arg is of type T for which std::is_bind_expression<T>::value == true (for example, another bind expression was passed directly into the initial call to bind), then bind performs function composition: instead of passing the function object that the bind subexpression would return, the subexpression is invoked eagerly, and its return value is passed to the outer invokable object. If the bind subexpression has any placeholder arguments, they are shared with the outer bind (picked out of u1, u2, ...). Specifically, the argument vn in the std::invoke call above is arg(std::forward<Uj>(uj)...) and the type Vn in the same call is std::result_of_t<T cv &(Uj&&...)>&& (cv qualification is the same as that of g).

对不起,此刻暂无示例.> _<

Sorry, no examples here at this moment. >_<

P.S.是的, std :: round 是一个重载函数,因此您应该进行类型转换以指定需要组成的重载.

P.S. And yes, std::round is an overloaded function so you should typecast it to specify which exactly overload you need to be composed.

这篇关于如何在c ++ 17中定义函数组成?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆