Phoenix 可以看作是对 Boost Lambda Library 的重新实现。它是作为 Boost.Spirit 的一部分的,但是我们完全可以把它作为单独的库来使用,以达到一些非常酷和方便的效果。
基本上,Phoenix 大大方便了在 C++ 中进行 functional programming。当然,由于 C++ 不是函数式语言,我们没有可能进行严格的 functional programming,库的作用也只是让这种风格得到更方便的实现。在 STL 里,我们已经在进行一些 FP,例如对 functor 的利用使得算法的重用性大大提高,而 Phoenix 则让编写 functor 更加自然和简便。
在下面的代码中,val(3) 和 val("Hello Phoenix") 都是 functor,它们的作用就是返回它们的参数。而且,它们是 lazily evaluated 的。
#include <iostream> #include <boost/spirit/include/phoenix_core.hpp> using namespace std; using namespace boost::phoenix; int main() { cout << val(3)() << endl; cout << val("Hello Phoenix")() << endl; return 0; }
输出:
3
Hello Phoenix
val 返回的是对参数的复制,但有时候我们需要返回的是引用,这时可以使用 ref:
#include <iostream> #include <boost/spirit/include/phoenix_core.hpp> using namespace boost::phoenix; using namespace std; int main() { int i = 3; char const* s = "Hello Phoenix"; cout << ref(i)() << endl; cout << ref(s)() << endl; return 0; }
输出与上面相同。
这两个例子显得有点太无趣了,lazy evalution 的好处在于,一个 functor 可以被保存起来以后再用,因此也可以“插入”到已有的算法当中。所以,如果你写了一个 print 算法,那这些 functor 就有用了:
#include <iostream> #include <boost/spirit/include/phoenix_core.hpp> using namespace std; using namespace boost::phoenix; template <typename F> void print(F f) { cout << f() << endl; } int main() { print(val(3)); print(val("Hello Phoenix")); return 0; }
输出结果与上面相同。
在 Boost Lambda Library 中, 有几个叫做 _1, _2, _3... 之类的好东西,它们可以代替第 n 个参数,例如 _2 就返回第2个参数。Phoenix 原本也是这么命名的,但是现在改成了 arg1, arg2, arg3...,它们都位于 boost::phoenix::arg_names 命名空间。看下面程序:
#include <iostream> #include <boost/spirit/include/phoenix_core.hpp> using namespace boost::phoenix; using namespace boost::phoenix::arg_names; using namespace std; int main() { int i = 3; char const* s = "Hello Phoenix"; cout << arg1(i) << endl; cout << arg2(i, s) << endl; return 0; }
输出仍然和上面相同。
下面这个例子比较有用,它去掉一个 vector 中所有的奇数,并且把结果打印出来。其中 remove_if 和 for_each 都是 STL 算法,它们都接受 functor,可以看到 Phoenix 让编写这些 functor 变得非常容易且直观。
#include <vector> #include <algorithm> #include <iostream> #include <boost/spirit/include/phoenix_core.hpp> #include <boost/spirit/include/phoenix_operator.hpp> using namespace boost::phoenix; using namespace boost::phoenix::arg_names; using namespace std; int main() { int init[] = {2, 10, 4, 5, 6, 1, 3, 9, 8, 7}; vector<int> c(init, init + 10); typedef vector<int>::iterator iterator; iterator cend = remove_if(c.begin(), c.end(), arg1 % 2 == 1); for_each(c.begin(), cend, cout << arg1 << " "); return 0; }
输出:
2 10 4 6 8