假設我們現在有個任務,要做乙個程式,將乙個陣列中的每乙個元素乘上100,然後賦值回去。為此,我寫下了這樣的c#**:
int ai=new int[10];
//初始化ai。
foreach(int i in ai)
我又寫了c++**:
vectorai(10);
//初始化ai。
for(vector::iterator i=ai.begin(); i!=ai.end(); ++i)
唔,**比c#長多了。c++真複雜。這個或許好點:
…for(int i=0; i
現在任務公升級了,有三個陣列,把乙個陣列的元素乘上第二個陣列相應元素,放入第三個陣列中。我想寫這樣的**(c#):
int ai1=new int[10],ai2=new int[10], ai3=new int[10];
//初始化ai1和ai2
foreach(int i in ai1, int j in ai2, int k in ai3)
//語法錯誤, 要能這麼寫
沒有這種語法,得用for:
…for(int i=0; i<0; ++i)
c++版的是這樣:
vectorai1(10), ai2(10), ai3(10);
//初始化ai1和ai2
for(int i=0; i<0; ++i)
**一樣。但是,c++有個transform演算法,可以不用迴圈:
…int mul_them(int v,int u)
transform(ai1.begin(), ai1.end(), ai2.begin(), ai3.begin(), mul_them);
**沒有簡單,可晦澀多了。不熟悉標準庫的初學者,會覺得這像是天書。我們一會兒來討論學習問題。
再加點難度,三個容器,第乙個是佇列,第二個是陣列,第三個是鍊錶。把佇列裡的元素乘上陣列裡的元素,放到煉表裡。c#佇列和鍊錶沒有隨機訪問操作符,必須利用ienumerable<>泛型介面訪問:
queueai1=new stack(10);
int ai2=new int[10]
linkedlistai3=new queue(10);
//初始化ai1和ai2
ienumeratoreai1=ai1.getenumerator();
ienumeratoreai2=ai2.getenumerator();
ienumeratoreai3=ai3.getenumerator();
while(eai1.movenext() && eai2.movenext() && eai3.movenext())
下面該c++**了。c++標準庫的佇列和鍊錶也沒有隨即操作符,必須使用迭代器:
dequeai1(10);
vectorai2(10);
listai3(10);
//初始化ai1和ai2
int mul_them(int v,int u)
transform(ai1.begin(), ai1.end(), ai2.begin(), ai3.begin(), mul_them);
c++也可以用手工迴圈訪問迭代器,就像c#那樣。當然,**會複雜得多,比c#的還複雜。
再提高難度,我發現這種運算在很多地方出現,想把它做成乙個演算法。但是,每次出現的三種容器的型別不一定,元素的型別也不同,我必須作乙個泛型演算法。先做c#的版本:
public class myalogrithm}}
有編譯錯誤。eai1.current和eai2.current分別返回t1和t2型別的例項,因為c#編譯器不知道t1和t2的型別,所以不能確定兩者是否能夠相乘。此路走不通,我正在努力解決這個問題。剩下一種辦法,就只能採用rtti(runtime type info)解決:
public class myalogrithm
else if(eai3.current.type==typeof(float)
&& eai1.current.type==typeof(int)
&& eai2.current.type ==typeof(int))
else if(eai3.current.type ==typeof(double)
&& eai1.current.type ==typeof(float)
&& eai2.current.type ==typeof(int))…}
}}回過頭來在看c++的實現:
template
r mul_them(t1 v,t2 u)
template
caculate_container(const c1& c1, const c2& c2, c3& c3)
就這麼簡單。由於模板是編譯期的機制,所有未確定型別(模板引數),是否包含所需的成員,如begin()、end()等,以及是否能夠相乘,編譯時便會立刻明了。而c#的泛型(確切地說,是.net的泛型),是跨語言的機制,泛型演算法可能會被其他語言使用,無法在編譯期確定型別的特徵,所以必須要求型別在編譯前明確所應具備的條件,即型別約束(where字句)。但問題還不在這裡。問題在於,c#的泛型的型別約束依賴於繼承,即型別必須從…類處繼承而來。而不是像c++09中的concept那樣直接描述型別所需的特徵。所以,c#版的泛型演算法也就是因此而失敗。
最後再做乙個小小的擴充套件,要求caculate_catainer<>()演算法可以接受乙個演算法,該演算法指示了如何對這些容器進行操作。此時,我們會發現,c#的泛型演算法反而能夠實現了:
public delegate void alg(t1 v1, t2 v2, r r);
public static void caculate_contain
(c1 c1, c2 c2, c3 c3, alga )
where c1: ienumerable
where c2 : ienumerable
where c3 : ienumerable
}//使用
public static void caculthem(int v1, int v2,int r)
caculate_contain(ai1, ai2, ai3, new alg(caculthem));
public static void caculthem2(float v1, int v2,double r)
caculate_contain(af1, ai2, ad3, new alg(caculthem2));
我使用了乙個委託,作為傳遞處理容器元素的演算法的載體。使用時,用具體的演算法建立委託的例項。但具體的演算法caculthem()必須同相應的容器元素型別一致。
下面輪到c++:
template
caculate_container(const c1& c1, const c2& c2, c3& c3, alg a)
//使用
template
r mul_them(t1 v,t2 u)
caculate_container(ai1, ai2, ai3, mul_them);
caculate_container(af1, ad2, ad3, mul_them);
如果容器元素有所變化,c#**必須重寫演算法caculthem()。但c++不需要,由於mul_them<>()本身是個函式模板,那麼只需將這個函式模板用新的型別例項化一下即可。
c++的**相對簡單些,靈活性也更高些。但這還不是全部,c++還有乙個最終極的解法,不需要迴圈,不需要建立模板演算法:
transform(c1.begin(), c1.end(), c2.begin(), c3.begin(), _1*_2);
沒看明白?我一開始也看不明白。這裡用到了boost庫的lambda表示式。_1佔位符對應c1的元素,_2的佔位符對應c2的元素,_1*_2表示才c1的元素乘上c2的元素,其結果放在c3裡。表示式可以寫得更複雜,比如(_1*_2+3*_1)/(_1-_2)。lambda表示式可以用在所有需要操作的演算法中,比如我要去掉字串中的「-」,可以這樣寫:
remove_if(s.begin(), s.end(), _1==』-』);
lambda
表示式基於一種叫做「模板表示式」的技術,通過操作符過載,將乙個表示式一層一層地展開,構成乙個解析樹。然後作為乙個函式物件傳遞給演算法,演算法在迴圈內呼叫函式物件,執行相應的計算。
被誤解的TCP
文 林沛滿 人一旦形成某種思維定勢,就很難再改變了。知道我收到最多的讀者來信是問什麼嗎?林工,有些tcp包發出去之後沒有看到對應的ack,算不算丟包啊?這個問題讓我很是好奇,明明rfc上沒有這樣的規定,為什麼總有讀者覺得每乙個資料報都應該有對應的ack呢?後來才注意到,很多提問者是做 開發出身的,已...
被誤解的外鏈判定標準
外鏈,是seo優化中比較重要的組成部分,雖然去年開始綠蘿演算法等搜尋引擎演算法的重制定對於外鏈的影響使得seoer一度質疑外鏈的重要性。但是,筆者小丹認為無論什麼時候好的外鏈永遠可以給 帶來驚喜。之前我們也曾分析過怎樣做外鏈,什麼樣的外鏈才能偶成為有效鏈結。那時候我們就突出乙個重點高質量鏈結,而製作...
使用DIV CSS的誤解解釋
1 用div css結構製作靜態html網頁等於徹底拋棄古老的table寫法 不全對,有時還是需要table 布局 之所以不建議用table來布局網頁是因為在網頁載入很慢的時候要等html table結構載入完成才能看到網頁,其次是table的布局沒有一定的語義,網頁 修改起來比較麻煩。但table...