2011年8月29日 星期一

讀書心得: 禮物

More about 禮物在人生中剛剛好的時刻,遇見一本書,提醒我許多已經忘記的事。溫暖的一本小書,簡短的故事,我在圖書館拿著書直接翻完了,卻比長篇大論更打動人心。

剛好我現在的年紀,開始會對以前做的一些事情懊悔,發現人生沒有那麼簡單,對未來又有一些迷惘和擔憂的時候,有時候就會被這些情緒佔用了我大部分的生活。

今天是上天賜與的禮物,這是為何他被叫作 當下(present) 的理由。這本小故事就是禮物,當你覺得事事不順的時候,就專注在當下的好事上吧,不要為過去自怨自艾,學習過去的失敗的經驗,去創造未來。

簡單再不過的事情,或許有點過於理想,幸福感多到漫出來了,重點是,你去實行了嗎?

2011年8月22日 星期一

讀書心得: The C++ Standard Library : a Tutorial and Reference

More about The C++ Standard Library
Effective C++第一條款就寫明「C++是一個語言聯邦。」 這個語言聯邦由四個次語言組成,分別是 C、 物件導向C++、Template C++、以及標準庫 STL。我去年接了大一程設課助教,每週都要上台教 C++,這份苦差事意外挖出不少自己當年學習上的盲點。其中最大的問題就是我對 C++ 的後兩個部分: Template C++以及 STL 不夠熟悉,只好找上這本書來補足這方面的知識。

STL 是一個相當淺明易用的程式庫,這從我以前亂逛 cplusplus.com,糊里糊塗就能隨便抄幾句 STL 來用就可以得證。本書對我的主要幫助不是學會 STL,而是能夠從宏觀的視野來看待整個 STL,了解當初 STL 設計的時候架構上的取捨,引發的優點以及缺點,能讀到這些設計上的觀點我覺得很難能可貴。就好像我以前都覺得 STL algorithm 異常難用,看了書才知道原來 STL algorithms 要搭配 function object 才能發揮威力。各種 Iterator 的錮中差異,也是看了此書後才有全盤了解。

內容上我認為第五章是整本書的核心精華,清楚說明了 STL 三大組件的關係與腳色
(圖片節錄自書上5.1節)

Container 負責管理物件集合,Algorithm 是操作手法,而 Iterator 則扮演此二者間的黏著劑,讓雙方可以透過抽象手法互相作用,不會有過緊的依賴關係,由此可以看出 STL 設計之初軟體架構就相當軟Q。接下來六 ~ 九章是書本的主力內容,分別對Container、Iterator、Algorithm 做專門深入的探討。

第八章 Function Object 我認為是值得一讀的特別章節,因為坦白說 function object 這東西使用上並不直覺 (我一直覺得只有聰明鬼才能想出替 object 加上operator( ) 來當函數呼叫的餿主意 ),但是要靈活地使用STL Algorithm,就一定要搭配 function object 才行。沒有function object,STL Algorithms 就只是彆腳程式庫。第十章之後還有介紹一些C++的其他標準庫,像是字串、I/O、國際化問題等等。

整體來講,這是一本好書,但是有點無聊。本書安排內容的方式是把 STL 各個部分切開來,每部份分配一章,依照主題中規中矩的逐一的細講下去,這樣寫的優點是以後要查閱很方便,想回顧某特定功能時很直接,但是缺點就是拿來當學習書會有點囉嗦,學習之初較易見樹不見林。不過書名就明顯寫了「a Tutorial and Reference」,除了拿來學習還可以當 reference 用,那難免有點這類弊病。

附帶一提這是德國人寫的英文書,所以文句很容易理解,幾乎沒有複雜難解又充滿詩意的句子,蠻適合當作練習閱讀原文書的材料。

相關文章連結
The C++ Standard Library讀書筆記:  Function Object

2011年8月21日 星期日

C++ Standard Library 讀書筆記: Function Object

我讀 C++ Standard Library: a Tutorial and Reference 後的筆記,主要內容來自 5.9小節,整個第八章,以及一些我自己的感想。

STL Algorithms

C++ STL Algorithms 裡一系列操作容器的函數,我一直以來都覺得這系列函數異常的難用。例如萬年範例 -- for_each 逐一打印容器的元素:
void print_int( int i ) {
    cout << i << endl;
}

for_each(v.begin(), v.end(), print_int); //逐一印出元素

乍看之下簡潔,但是這 for_each 有個致命的缺點,就是沒辦法傳入額外參數。例如我想對集合內的每個數值都加上一個固定值,用 for 迴圈再直覺不過:
int value = 5;
vector<int>::iterator it;
for(it=v.begin(); it!=v.end(); ++it)
    *it = *it + value;

這麼簡單的程式,想要改寫成for_each版本,馬上碰壁:
void add_value( int & i ) {
    i = i + ???;  // add something?
}

int main() {
    vector<int> v;
    for_each(v.begin(), v.end(), add_value);
}
問號的地方只能放常數或者全域變數,而兩個選項都很爛,我真正想要的是從 main 裡面傳入一個變數。所以很長一段時間我把 for_each 封印起來,乖乖自己寫 for 迴圈。

Function Object

直到最近,我才明瞭到 function object 可能是這個惱人的問題的答案。

所謂的 function object 就是一個object,但是實做了operator() 。它實際上是物件,但可以當作函數來呼叫。要發揮整個 STL Algorithms 的威力,就一定要瞭解 function object。萬年打印範例用funciton object 來實作就會長這樣子 :
class PrintInt{
    void operator() (int i) const {
        cout << i;
    }
};

for_each(v.begin(), b.end(), PrintInt());

PrintInt 就是一個 function object,它有實作operator()。 我們可以宣告一個物件變數 PrintInt p;  對他做好像函數呼叫的動作 p(); 而此時實際上呼叫的是 PrintInt::operator() 這個成員函數。 function object 最大的好處就是裡面可以定義成員變數,維護內部的狀態,而一般的函式做不到。我們回頭拿它來解決剛剛的惱人問題,就可以明白我在說什麼。這兒定義了一個很簡單的小物件AddValue,因為AddValue有實作operator() ,所以它是一個function object。
class AddValue {
    int value;
public:
    AddValue(int v) { value = v; }
    void operator()(int &i) { i = i+value; }
};

int main() {
    vector<int> v;
    for_each(v.begin(), v.end(), AddValue(5));
}

把AddValue物件當作參數傳入, 此時 for_each 針對集合元素的每次 function call 實際上是呼叫 AddValue::operator() ,而我們希望加上去的固定值,被當做 function object 的成員變數存起來了。這樣就巧妙的解決了額外參數的問題。

AddValue add10(10), add30(30); //建立許多function object在不同狀況使用
for_each(v.begin(), v.end(), add10);
for_each(v.begin(), v.end(), add30);

求平均值

「求平均值」是比較進階的例子,用 for_each 來計算整數集合的平均值。我們利用了 for_each 最後會回傳該 function object 的特性,把元素的總和(sum) 跟元素個數(num_element) 都紀錄在的function object的成員變數中,最後取平均值 :
class MeanValue {
    int sum;
    int num_element;
public:
    MeanValue() { sum = num_element = 0;}
    void operator() (int i) { sum += i; num_element++; }
    double value() { return (double)sum / (double)num_element); }
};

int main() {
    vecter<int> v;
    MeanValue m = for_each(v.begin(), v.end(), MeanValue());
    cout << m.value();
}

Function Object的優點

Function Object主要有三個優點
  1. Function Object 比一般函數聰明,它可以維護內部的狀態,記錄呼叫過程中發生的事。
  2. 通常 Function Object 的效能比函數指標好,因為比較容易最佳化。
  3. Function Object 可以當作 template 參數使用,一般的函數不行。

預先定義好的Function Object

除了自己寫 function object 以外,STL已經定義好了一系列常用的 function object 。這裡舉兩個簡單的範例,欲知詳情可以參閱 C++ Reference "functional"
int myint[] = {20,30,10,50,40};
vector<int> v(myint, myint+5);

sort( v.begin(), v.end(), less<int>() ); //排序由小到大
sort( v.begin(), v.end(), greater<int>() ); // 排序由大到小

remove_if( v.begin(), v.end(), bind2nd( greater<int>(), 40)); //移除所有大於40的元素