C++使用函數式代替泛型迭代器設計接口

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;
    }
}

上面的例子可以看出,函數式與面向對象式程序設計,都是程序設計的一種方法。函數式與面向對象式,很多時候只是考慮問題的角度不同。沒有一種永遠好的方法,我們應該做的,是巧妙利用各種方法來解決各類問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章