C++14函数柯里化

柯里化的详细介绍

在维基百科里看到柯里化的Javascript例子:

var foo = function(a) {
  return function(b) {
    return a * a + b * b;
  }
}

这样调用上述函数:(foo(3))(4),或foo(3)(4)。觉得很有意思,没想过还有这样的调用方式,于是用C++写了一个(以下均为C++14):

auto foo(int a) {
    return [a](int b) { return a * a + b * b; };
}

于是想到按着这个思路可不可以利用泛型和递归的思想实现对任意函数的柯里化呢,先来一个简单的求和函数和它的柯里化之后可能的样式:

int sum(int a, int b, int c) {
    return a + b + c;
}

//柯里化后可能的样式
auto sumCurring(int a) {
    return [a](int b) {
        return [a, b](int c) {
            return sum(a, b, c);
        };
    };
}

能不能一次将所有参数都化为单个参数,写了递归后发现我错了,这样的递归每次的返回值都不一样,是不同阶函数。转变一下思路,一次只对一个参数柯里化。将样式修改一下:

auto sumCurring(int a) {
    return [a](int b, int c) {
        return sum(a, b , c);
    };
}

再将函数作为参数传入,两种方式,一是函数指针,二是官方functional库。为了对所有函数的支持,使用第二种。对上述函数再包装一次:

#include <functional>
using namespace std;
auto funcCurrying(function<int(int, int, int)>& func) {
    return [func](int a) {
        return [func, a](int b, int c) {
            return func(a, b, c);
        };
    };
}

到这一层就和sum函数没多大关系了,只要是int(int, int, int)形式的函数就行了。于是自然的引出了泛型,为了区分函数的第一个参数和其他参数,将Args分为First和OtherArgs:

#include <functional>
using namespace std;
template<typename Res, typename First, typename... OtherArgs>
auto funcCurrying(function<Res(First, OtherArgs...)> func) {
    return [func](First first) {
        return [func, first](OtherArgs... otherArgs) {
            return func(first, otherArgs...);
        };
    };
}

结果使用时无法编译:

auto sum_c = funcCurrying(sum);//此句无法编译,参数不匹配

卡在这很久,在网上看到他人写的柯里化,都引入了很多其他的东西,不是我想要的效果。内涵越少,外延越大,不引入太多的特性,才能适用更多的情况。最终找到C++14新增的特性——可以使用auto关键字定义参数。最终柯里化函数及实例如下:

#include <iostream>

using namespace std;

int sum(int a, int b, int c){
    return a + b + c;
}

/**
 * 函数柯里化
 * 适用于参数个数大于1的函数
 * @param func 待柯里化函数
 * @bug 不适用于零参数和单参数 当然也不能curring(curring)
 */
auto currying = [](auto func) {
    return [func](auto first) {
        return [func, first](auto... otherArgs) {
            return func(first, otherArgs...);//func 待柯里化函数 first 首个参数 otherArgs 其他参数
        };
    };
};

int main() {
    auto sum_currying = currying(sum);
    auto sum_1 = sum_currying(1);
    cout << sum_1(6, 66) << endl;

    auto sum_1_6 = currying(sum_1)(6);
    auto sum_99 = sum_currying(99);
    auto sum_99_44 = currying(sum_99)(44);
    cout << sum_1_6(66) << endl;
    cout << sum_99_44(66) << endl;
    return 0;
}

输出:

73
73
209
知识共享许可协议
本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。

发表评论

电子邮件地址不会被公开。 必填项已用*标注