C++ 标准库使用迭代器作为索引下一值的部件。这在许多方面是有利的。
比如我们有一个生成平方列表的函数:
template <typename _Iter>
vector<int> get_square_list(_Iter begin, _Iter end)
{
vector<int> v;
for (auto iter = begin; iter != end; ++iter) {
auto e = *iter;
v.push_back(e * e);
}
return v;
}
int main(void)
{
vector<int> v { 1, 2, 3, 4, 5 };
list<int> l { 5, 4, 3, 2, 1 };
auto vlist = get_square_list(v.begin(), v.end());
auto llist = get_square_list(l.begin(), l.end());
for (auto e : vlist) {
cout << e << endl;
}
for (auto e : llist) {
cout << e << endl;
}
}
以上示例证明,在泛型编程中,使用模板+迭代器的接口,具有很大的优势。
但是,当我们将泛型迭代器引入类中,作为对象保存时,优势就没那么明显了:
template <typename _Iter>
class GetNextSquareValue
{
public:
GetNextSquareValue(_Iter begin, _Iter end)
: begin(begin), end(end), iter(begin) {}
int get_next() {
assert(iter != end);
auto e = *iter;
++iter;
return e * e;
}
private:
_Iter begin;
_Iter end;
_Iter iter;
};
int main(void)
{
vector<int> v { 1, 2, 3, 4, 5 };
list<int> l { 5, 4, 3, 2, 1 };
GetNextSquareValue<vector<int>::iterator> vlist(v.begin(), v.end());
GetNextSquareValue<list<int>::iterator> llist(l.begin(), l.end());
for (int i = 0; i != v.size(); ++i) {
cout << vlist.get_next() << endl;
}
for (int i = 0; i != l.size(); ++i) {
cout << llist.get_next() << endl;
}
}
如上代码实现了列表的部分转换。(只有当调用get_next()的时候才进行转换。)因为是惰性求值,保存求值的状态是很重要的。
显然,这里 vlist 和 llist 并不是一个类型,不利于保存。当然,我们可以构建基类、构建通用迭代器等来解决这个问题。但是,与其繁琐地寻求简便地保存迭代器处理办法,为什么不在一开始就不保存迭代器呢?
class GetNextSquareValue
{
public:
GetNextSquareValue(std::function<int ()> get_next_value)
: get_next_value(get_next_value) {}
int get_next() {
auto e = get_next_value();
return e * e;
}
private:
std::function<int ()> get_next_value;
};
int main(void)
{
vector<int> v { 1, 2, 3, 4, 5 };
list<int> l { 5, 4, 3, 2, 1 };
auto viter = v.begin();
auto liter = l.begin();
GetNextSquareValue vlist([&]() {
assert(viter != v.end());
auto r = *viter;
++viter;
return r;
});
GetNextSquareValue llist([&]() {
assert(liter != l.end());
auto r = *liter;
++liter;
return r;
});
for (int i = 0; i != v.size(); ++i) {
cout << vlist.get_next() << endl;
}
for (int i = 0; i != l.size(); ++i) {
cout << llist.get_next() << endl;
}
}
C++ 11 支持了 lambda 表达式。 lambda 表达式可以便利地实现函数式程序设计。如上就是一个很好的 class 与 lambda 结合的例子。
当然,我们也可以进一步地,完全使用函数式来编写上述程序(其中使用了C++14的auto返回值):
template <typename _Iter>
auto make_iterNext(_Iter begin, _Iter end)
{
_Iter iter = begin;
return [=]() mutable {
assert(iter != end);
auto r = *iter;
++iter;
return r;
};
}
auto make_getNextValue(std::function<int ()> get_next_value) {
return [=]() mutable {
auto e = get_next_value();
return e * e;
};
}
int main(void)
{
vector<int> v { 1, 2, 3, 4, 5 };
list<int> l { 5, 4, 3, 2, 1 };
auto vf = make_getNextValue(make_iterNext(v.begin(), v.end()));
auto lf = make_getNextValue(make_iterNext(l.begin(), l.end()));
for (int i = 0; i != v.size(); ++i) {
cout << vf() << endl;
}
for (int i = 0; i != l.size(); ++i) {
cout << lf() << endl;
}
}
上面的例子可以看出,函数式与面向对象式程序设计,都是程序设计的一种方法。函数式与面向对象式,很多时候只是考虑问题的角度不同。没有一种永远好的方法,我们应该做的,是巧妙利用各种方法来解决各类问题。