C++使用函數式代替泛型迭代器設計接口
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;
}
}
上面的例子可以看出,函數式與面向對象式程序設計,都是程序設計的一種方法。函數式與面向對象式,很多時候只是考慮問題的角度不同。沒有一種永遠好的方法,我們應該做的,是巧妙利用各種方法來解決各類問題。