C++中for_each的應用


C++中for_each的應用

 

for each語法是方便的,也是很自然的,這也是為什么很多語言都有這樣的語法,就我所知,包括java(jdk5.0以上),python,php,asp.net等語言都有類似的語法,甚至微軟為C++/CLI中也添加了這樣的語法。但是很遺憾的是,C++98標准中沒有,於是,我們只能通過可悲的for_each算法去模擬。。。。。。。。。。先看看原生的語法是多么方便和自然的吧,雖然有人將其視為語法糖,但是,就算是糖,這也是很甜的那種。

先看看Python中的循環,雖然不是for each,但是類似於。

l = [1,2,3,4,5]

for i in l:

print i

簡潔,干凈,

假如你有幸使用微軟的托管C++,你可以使用類似的語法:

using namespace System;

 

#include <list>

#include <iostream>

using namespace std;

 

int main()

{

    int a[5] = {1,2,3,4,5};

    list<int> l(a, a+5);

 

    for each ( int i in l)

       Console::Write(i);

 

 

    system("PAUSE");

}

雖然作為強類型語言,在聲明方面稍微復雜點,循環的處理還是那么簡潔,干凈。

 

再來看看現有的C++中的:

#include <list>

#include <iostream>

using namespace std;

 

int main()

{

    int a[5] = {1,2,3,4,5};

    list<int> l(a, a+5);

 

    // 同樣是需要輸出

    for(list<int>::const_iterator lit = l.begin(); lit != l.end(); ++lit)

    {

       cout <<*lit <<endl;

    }

 

    system("PAUSE");

}

繁復到我都不想說了,list<int>::const_iterator似的迭代器聲明語法不符合一處定義的原則,冗余信息太多。(C++09添加的auto用法就是解決此問題的),即便是解決了此問題,還是會發現,在C++中寫個循環比在python(僅僅是一個例子,其他有類似for each特性的語言都比C++簡單)中復雜太多了。而循環實在是太過於常見的語法了,所以一次又一次使用這種本可以簡單,但是受限於語法而搞得這么復雜的C++可憐語法的時候,我總是忍不住想要吐血。對於這么簡單的例子,我們是可以找到一些方法來稍微簡化一點的。沒有for each語法,我們起碼還有for_each算法-_-!

於是可以這樣:

#include <list>

#include <iostream>

#include <algorithm>

using namespace std;

 

void printInt(int i)

{

    cout <<i <<endl;

}

 

int main()

{

    int a[5] = {1,2,3,4,5};

    list<int> l(a, a+5);

 

    // 同樣是需要輸出

    for_each(l.begin(), l.end(), ptr_fun(printInt));

 

    system("PAUSE");

}

 

在加大了理解難度后(本來for each語法多簡單啊,現在還要理解ptr_fun這樣的函數對象生成的輔助函數),我們的循環是稍微簡單一點了,雖然在這個例子中我們甚至要額外寫函數-_-!雖然說函數可以只寫一次,循環可是常常用的啊。

對於這樣簡單的例子,已經可以看出沒有for_each語法的痛苦了,再復雜一點的例子

 

對於類成員函數的調用,看看有for_each的情況

python中:

class Add():
    def __init__(self, i):
        self._i = i
    def add(self):
        self._i += 1
    def __str__(self):
        return str(self._i)
    
s = [Add(1), Add(2), Add(3)]

for a in s:
    a.add()

for a in s:
    print a

 

這里拆分成兩個函數,可以看出我的無奈,想要在一個for_each語法中連續調用兩個函數的方法。。。。目前只有再寫一個函數,而這個函數的作用就是僅僅調用這兩個函數提供給for_each使用。不說這些喪氣+無奈的話了,光是調用一個類的成員函數的可能還是有的。

C++中:

#include <list>

#include <iostream>

#include <algorithm>

#include <cstdio>

using namespace std;

 

void printInt(int i)

{

    cout <<i <<endl;

}

 

class CAdd

{

public:

    CAdd(int ai):mi(ai) { }

 

    void add() { ++mi; }

 

    operator int() { return mi;}

    int mi;

};

 

int main()

{

    CAdd a[3] = { CAdd(1), CAdd(2), CAdd(3)};

    list<CAdd> l(a, a+3);

 

    // 同樣是需要輸出

    for_each(l.begin(), l.end(), mem_fun_ref(&CAdd::add));

    for_each(l.begin(), l.end(), ptr_fun(printInt));

 

    system("PAUSE");

}

為了實現循環的簡潔,重新引入了新的復雜度,mem_fun_ref,希望一般的C++程序員見過這樣的函數對象輔助函數。。。。還多了類似&CAdd::add這樣的成員函數指針的語法,希望一般的程序員也能理解。。。。(不提有for each語法的語言中除了for each這樣自然的語法外,做多復雜的運算都沒有引入任何新的復雜度),最主要的是,你想要在一條for_each中實現兩個函數的調用,你除了老老實實的實現一個新的函數外,就是像我這樣了,調用for_each兩次,兩種方法都是不那么容易讓人接受。。。。。。。。但是,在現有的C++中,我們也就只能做到這樣了。既然用C++,就接受現實吧。其實,顯示遠比一般人想象的要復雜。

以上情況還是函數沒有參數的時候,當函數有參數的時候,新的問題又來了。

看看python中這樣一個簡單的功能:

def add(a,b):
    return a + b

l = [1,2,3,4,5]
for i in l:
    print add(i,1)

 

無非就是在每個輸出的函數中調用一個函數,沒有任何值的一提的地方,是個人就能看懂。

在C++需要實現成下面這個樣子:

#include <list>

#include <iostream>

#include <algorithm>

#include <functional>

using namespace std;

 

template <typename T>

class Add : public binary_function<T, T, void>

{

public:

    void operator()(const T& ai, const T& aj) const

    {

       cout <<(ai + aj) <<endl;

    }

 

};

 

int main()

{

    int a[5] = {1,2,3,4,5};

    list<int> l(a, a+5);

 

    for_each(l.begin(), l.end(), bind2nd(Add<int>(), 1));

 

    system("PAUSE");

}

 

到這一步,我希望大部分的C++程序員還能看懂什么意思及其實現的機制。。。。但是僅僅是我的希望吧,甚至我懷疑,這樣的實現放在工作中,總監和老總是不是會將我批的體無完膚,的確,為了省略一個循環值得這樣做嗎?實在不值得,但是C++提供給你的機制就是這樣。Add這樣的函數對象構造復雜,還得利用trail機制(從binary_function類繼承過來),然后再利用函數適配器bind2nd/bind1st,這樣的東西似乎需要語言專家來解釋,我是解釋不清楚了,再加上更加復雜的函數連標准庫中的bind都肯定不夠用,還只能用boost::bind庫,去試試吧,然后會發現一般的函數指來指去(特別是類成員)用的太復雜了,還是用boost::funciton吧。。。。。似乎永無止境。但是有了for each語法,那么什么復雜度都沒有。。。。。還想自虐嗎?算了吧,我基本上已經放棄了。不給糖吃,也放不着自己開工廠制作。。。。

另外,對於能夠用boost的兄弟們,糖是有的吃的。boost:: foreach庫即是如此。

 

下面是boost:: foreach的例子

#include <list>

#include <iostream>

#include <boost/foreach.hpp>

using namespace std;

 

 

int main()

{

    int a[5] = {1,2,3,4,5};

    list<int> l(a, a+5);

 

    BOOST_FOREACH( int i, l)

    {

       cout <<i <<endl;

    }

 

    BOOST_FOREACH( int i, l)

    {

       cout <<i+1 <<endl;

    }

 

    system("PAUSE");

}

 

就算僅僅這一個例子。。。。永遠不要怪庫開發者(比如boost,ace,loki)將C++語言弄得多么扭曲,他們也是出於無奈。。。。別去看實現,先只管用吧。

對於不能用boost的我。。。。只能看有沒有辦法偷偷的將/cli編譯選項打開了。。。^^


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM