网站设计公司佛山,深圳app开发公司有哪些,wordpress影视采集网站,微信电影小程序搭建链接 http://geek.csdn.net/news/detail/96636
概述 函数式编程是一种编程范式#xff0c;它有下面的一些特征#xff1a;
函数是一等公民#xff0c;可以像数据一样传来传去。 高阶函数 递归 pipeline 惰性求值 柯里化 偏应用函数 C98/03中的函数对象#xff0c…链接 http://geek.csdn.net/news/detail/96636
概述 函数式编程是一种编程范式它有下面的一些特征
函数是一等公民可以像数据一样传来传去。 高阶函数 递归 pipeline 惰性求值 柯里化 偏应用函数 C98/03中的函数对象和C11中的Lambda表达式、std::function和std::bind让C的函数式编程变得容易。我们可以利用C11/14里的新特性来实现高阶函数、链式调用、惰性求值和柯理化等函数式编程特性。本文将通过一些典型示例来讲解如何使用现代C来实现函数式编程。
高阶函数和pipeline的表现形式 高阶函数就是参数为函数或返回值为函数的函数经典的高阶函数就是map、filter、fold和compose函数比如Scala中高阶函数
map
numbers.map((i: Int) i * 2) 对列表中的每个元素应用一个函数返回应用后的元素所组成的列表。
filter
numbers.filter((i: Int) i % 2 0) 移除任何对传入函数计算结果为false的元素。
fold
numbers.fold(0) { (z, i) a i } 将一个初始值和一个二元函数的结果累加起来。
compose
val fComposeG f _ compose g _ fComposeG(“x”) 组合其它函数形成一个新函数f(g(x))。
上面的例子中有的是参数为函数有的是参数和返回值都是函数。高阶函数不仅语义上更加抽象泛化还能实现“函数是一等公民”将函数像data一样传来传去或者组合非常灵活。其中compose还可以实现惰性求值compose的返回结果是一个函数我们可以保存起来在后面需要的时候调用。
pipeline把一组函数放到一个数组或是列表中然后把数据传给这个列表。数据就像一个链条一样顺序地被各个函数所操作最终得到我们想要的结果。它的设计哲学就是让每个功能就做一件事并把这件事做到极致软件或程序的拼装会变得更为简单和直观。 Scala中的链式调用是这样的
s(x) (1 to x) | filter (x x % 2 0) | map (x x * 2) 用法和Unix Shell的管道操作比较像|前面的数据或函数作为|后面函数的输入顺序执行直到最后一个函数。
这种管道方式的函数调用让逻辑看起来更加清晰明了也非常灵活允许你将多个高阶函数自由组合成一个链条同时还可以保存起来实现惰性求值。现代C实现这种pipeline也是比较容易的下面来讲解如何充分借助C11/14的新特性来实现这些高阶函数和pipeline。
实现pipeline的关键技术 根据前面介绍的pipeline表现形式可以把pipeline分解为几部分高阶函数惰性求值运算符|、柯里化和pipeline把这几部分实现之后就可以组成一个完整的pipeline了。下面来分别介绍它们的实现技术。
高阶函数 函数式编程的核心就是函数它是一等公民最灵活的函数就是高阶函数现代C的算法中已经有很多高阶函数了比如for_each, transform
std::vector vec{1,2,3,4,5,6,7,8,9} //接受一个打印的Lambda表达式 std::for_each(vec.begin(), vec.end(), [](auto i){ std::cout
define define_functor_type(func_name) class tfn_##func_name {\
public: template
define make_globle_functor(NAME, F) const auto NAME define_functor_type(F);
//test code make_globle_functor(fn_add, add); make_globle_functor(fn_add_one, add_one);
int main() { fn_add(1, 2); fn_add_one(1);
return 0; } make_globle_functor生成了一个可以直接使用的全局函数对象使用起来更方便了。用这个方法就可以将普通函数转成pipeline中的函数对象了。接下来我们来探讨实现惰性求值的关键技术。
惰性求值 惰性求值是将求值运算延迟到需要值时候进行通常的做法是将函数或函数的参数保存起来在需要的时候才调用函数或者将保存的参数传入函数实现调用。现代C里已经提供可以保存起来的函数对象和lambda表达式因此需要解决的问题是如何将参数保存起来然后在需要的时候传给函数实现调用。我们可以借助std::tuple、type_traits和可变模版参数来实现目标。
template
define tfn_chain fn_chain()
//test code void test_pipe() { auto f1 [](int x) { return x 3; }; auto f2 [](int x) { return x * 2; }; auto f3 [](int x) { return (double)x / 2.0; }; auto f4 [](double x) { std::stringstream ss; ss x; return ss.str(); }; auto f5 [](string s) { return “Result: ” s; }; auto compose_fn tfn_chain|f1|f2|f3|f4|f5; //compose a chain compose_fn(2); // Result: 5 } 测试代码中用一个fn_chain和运算符|将所有的函数组合成了一个函数链在需要的时候调用从而实现了惰性求值。
fn_chain的实现思路是这样的内部有一个std::tuple
template
define make_globle_curry_functor(NAME, F) define_functor_type(F); const auto NAME fn_to_curry_functor(tfn_##F());
make_globle_curry_functor(map, fn_map); make_globle_curry_functor(reduce, fn_reduce); make_globle_curry_functor(filter, fn_filter); 我们定义了map、reduce和filter支持柯里化的三个全局函数对象接下来我们就可以把它们组成一个pipeline了。
void test_pipe() { //test map reduce vector slist { “one”, “two”, “three” };
slist | (map [](auto s) { return s.size(); })| (reduce 0 [](auto a, auto b) { return a b; })| [](auto a) { cout a endl; };//test chain, lazy eval
auto chain tfn_chain | (map [](auto s) { return s.size(); })| (reduce 0 [](auto a, auto b) { return a b; })| ([](int a) { std::cout a std::endl; });slist | chain;} 上面的例子实现了pipeline的mapreduce这个pipeline支持currying还可以任意组合非常方便和灵活。
有了这个pipeline实现灵活的AOP也是很容易的
struct person { person get_person_by_id(int id) { this-id id; return *this; }
int id;
std::string name;}; void test_aop() { const person p { 20, “tom” }; auto func std::bind(person::get_person_by_id, p, std::placeholders::_1); auto aspect tfn_chain | ([](int id) { cout “before”; return id 1; }) | func | ([](const person p) { cout “after” endl; });
aspect(1);} 上面的测试例子中核心逻辑是func函数我们可以在func之前或之后插入切面逻辑切面逻辑可以不断地加到链条前面或者后面实现很巧妙使用很常灵活。
总结 本文通过介绍函数式编程的概念入手分析了函数式编程的表现形式和特性最终通过现代C的新特性和一些模版元技巧实现了一个非常灵活的pipeline展示了现代C实现函数式编程的方法和技巧同时也提现了现代C的强大威力和无限的可能性。文中完整的代码可以从我的GitHubhttps://github.com/qicosmos/cosmos/blob/master/modern_functor.hpp上查看。
本文的代码和思路参考和借鉴了http://vitiy.info/templates-as-first-class-citizens-in-cpp11/在此表示感谢。