萬眾囑目的 COSCUP 2008 開始接受報名了!
今年在 Knight、Bob、及多位籌備人的奔走之下,拉到破記錄的贊助!結餘款會用來支持本土社群的活動。
為了讓贊助單位錢花得過癮,以後還想再花,社群朋友在現場請花點時間(如吃茶喝點心的時候)找贊助單位的人聊聊、把贊助單位的傳單搶光,如果剛好在騎驢找馬,就留個聯絡資料給你有興趣的公司吧!一定比三位數或四位數的網站有效的!
2008年8月2日
COSCUP 2008 開始報名囉!
2008年7月27日
一蹶不振
啊,不是我跌跤了從此爬不起來,好友們不要擔心...
彤彤寫他的暑假作業,有一部分是含有破音字的成語,我希望他一個一個查,不要唸錯了。查到的第一個就是「一蹶不振」。這個成語我從小都是唸
|ˋ ㄐㄩㄝˊ ㄅㄨˊ ㄓㄣˋ但教育部可不這麼想!【教育部成語典--修訂本成語】給的注音是
| ㄐㄩㄝˊ ㄅㄨˋ ㄓㄣˋ哇哇哇!原來我的國語爛成這樣!四個字的成語可以唸錯兩個字,慚愧!
「一」和「不」倒底該怎麼唸?顯然要看後面跟的字是幾聲:
- 一張紙、一船人、一把蔥、一塊玉、一個人
- 不乖唷、不行吧、不好囉、不要啦
看看教育部的國語小字典怎麼寫:
喂喂喂!教育部,你要教小朋友,可不可以說白話?陰平、陽平、上聲、去聲是什麼,小朋友學過嗎?幹麼不寫一聲、二聲、三聲、四聲?彤彤五年級才第一次在上課時聽到這些東西,老師還說不懂沒關係,以後還會教,這樣子你小字典首頁寫
本典適用於國小學童及教師,若為其他需求,請詳閱說明。是開玩笑的嗎?我找了半天,才在建中的網站找到這篇明快解釋陰平、陽平、上聲、去聲的網頁,一到五年級的小朋友如何能懂?
我來當義工家長,翻譯一下好了:
- 「一」字單用或在一詞一句的末尾,念一聲;在四聲字前,念二聲;在一聲、二聲、三聲之前,念四聲。
- 「不」字在四聲字之前,變讀為二聲。
教育部成語典的注音是錯的!
原來帶給我們「三隻小豬」的教育部成語典竟是如此一蹶不振!
2008年5月4日
當 C++ 的不變性遇上懶人法
聊聊我最近碰到的一個問題:想用懶人法(lazy initialization)提升 C++ 程式的效率,郤碰上不變性(constness)的阻礙,和解決的方法。
寫 C++ class 的時候,常常會寫一些 getter 和 setter 來讀寫物件的屬性,比方說 Person 這個物件有年齡(age)屬性,就可以寫個 getAge() 和 setAge() 來讀取和改變它的值。典型的寫法大約是這樣:
class Person {
Person(int persion_id, PersonDatabase & pdb); // 從資料庫裡抓個人資料進來
...
int getAge(void) const { return age_; } // 傳回 age 資料
void setAge(int age) { age_ = age; } // 改變 age 資料
...
};
Person::Person(int person_id, PersonDatabase & pdb)
{
// 把所有屬性從資料庫讀回來填好
}
這是常見的設計:在 constructor 把所有的屬性填好,並提供 getter 和 setter 函式供其他物件讀寫。在 getAge() 的宣告加上了 const 表示這個 member function 不會改變 Person 物件的屬性。一開始就把不變性說清楚是比較好的設計,這樣子任何程式都可以呼叫 alice.getAge() 來讀出 alice 的年齡,不論 alice 這個 Person 物件是不是 const 的。
想像政府某單位有個程式要把全國納稅人資料載入,然後對年齡、繳稅金額、教育程度的數據做統計分析。依以上 Person class 的設計,那大概是這樣:
vector<Person>這樣做程式很簡單,但有兩個問題:people;
PersonDatabase pdb;
while (pdb.next()) {people.push_back(Person(pdb.getId(), pdb));
}
- 這樣會在記憶體中建立一千多萬個 Person 物件,每個物件如果需要 100 bytes,那就至少需要 1GB 的記憶體。用現代的個人電腦來看不算多,但如果要處理的國家不是台灣而是日本或美國,那就大多了。
- 另一個問題是把 100 個 bytes 讀進來,依身分證號碼、年齡、姓、名、地址、繳稅金額、教育程度等等屬性分門別類存在每個 Person 物件的肚子裡,也需要時間。既然我們只要用年齡、繳稅金額、教育程度三項數據做統計,花那美國時間去多讀一堆用不到的屬性、還浪費那美國空間去把這些屬性分門別類存好... 好像蠻白痴的。
所以這個最簡單的設計不 scalable,在物件內含資料較多或有巨量的同種物件要載入記憶體時會浪費記憶體,也變得很慢。
為了空間和時間的效率,一個普遍的技巧是懶人法: lazy initialization,也有人說 on-demand initialization,做法就是在建立 Person 物件時懶惰一下,不去把所有資料讀進來,等 getter 被呼叫的時候,如果沒有資料,再去讀進來存著和回傳,下次同一個 getter 再被呼叫時就可以直接回傳。這個做法需要多一點程式碼,每個 getter 也會要多花一丁點時間檢查有沒有資料,但可能省下不少空間和總時間。(但如果讀資料庫很慢很慢,可能就不適合這樣做。)
程式大概寫成這樣:
Person::Person(int person_id, PersonDatabase & pdb)這樣太棒了!一個設計可以省時間又能省空間。一切都很美好...
{
// 把所有要從資料庫讀的屬性設成「不詳」
age_ = -100;
...
// 記住資料庫在哪裡,以後可以用
pdb_ = pdb;
}
Person::loadInt_(char * name)
{
// 從 pdb_ 載入指定的屬性,轉成 int 後回傳
}
int Person::getAge(void) const
{
if (age_ < 0) age_ = this->loadInt_("age"); // loadInt_() 會去讀 PersonDatabase 回傳 int
return age_;
}
碰!
person.cc: In member function `int Person::getAge() const':Compile 失敗了!為什麼?
person.cc:77: error: passing `const Person' as `this' argument of `int
Person::loadInt_()' discards qualifiers
make: *** [person.o] Error 1
因為在 getAge() 裡呼叫 loadInt_() 破壞了不變性。
getAge() 明明昭告天下是不會修改 Person 物件的,但為了用懶人法,又不得不在沒值時叫 loadInt_() 來把年齡抓回來交差,換句話說,getAge() 會修改 Person 的 age_ 屬性。
Compiler 不准你做這種事。怎麼辦?
這時在觀念上要把介面和實作分開。宣告是介面,實作要在符合宣告的精神下達成任務。
getAge() 的宣告可以解釋成:
致所有呼叫 getAge() 的程式:我謹以至誠,不會改變 Person 物件的內容,alice 是 19 歲,我就會回傳 19,而且不會改動任何 alice 的屬性。照這個宣告的精神,雖然 getAge() 要把年齡屬性載進來,把 alice 的年齡從不詳變成 19 才回傳,但這個行為沒有違反上面的宣告,回傳「不詳」才是違反 getAge() 自己的諾言。
所以程式要這樣寫:
int Person::getAge(void) const先用 const_cast 把 Person 物件 this 從 const Person * 轉成非 const 的 Person *,就可以呼叫 loadInt_() 了。
{
if (age_ < 0) age_ = const_cast<Person *>(this)->loadInt_("age"); // 把 this 指標轉成 non-const
return age_;
}
要違反自己在語法上的宣告,才能符合宣告的精神,這真是 C++ 的黑暗面啊~~~
2008/6/11 後記:謝謝 fr3@k 的指教,在這種情形用 mutable 關鍵字宣告 age_ 就可以在 const 的 member function 中改動 age_ 了。
網路上有不少文章講 mutable vs. const_cast,我看了幾篇,都是一面倒不建議使用 const_cast,其中一個論點和 compiler 最佳化有關。最佳化是個很大的議題,我不太懂,但如果 const_cast 的使用造成 compiler 不知道這個 const member function 會改到 age_ 而把它最佳化成直接回傳某個 register 中的值,問題就大了。(喂!寫程式寫到要顧慮 compiler 的 bug,會不會太辛苦了點?)
不過我實在不喜歡在 header 檔中宣告 mutable int age_,因為 age_ 是不是 mutable 是實作的細節,不是介面,如果換一個實作也許就不需要用 mutable,實在不想在 header 檔裡寫 mutable。看來要用 pImpl pattern 了... 但是用 pImpl 還是躲不掉 mutable... Orz
class Person {
...
struct Impl;
mutable Impl * pImpl_;
};
訂閱:
文章
(
Atom
)