11/22/2009 2:20:00 AM

終於突破單日 100KM 了 :D (台北 <--> 大溪)

543 | 安德魯的當年勇

自從上個月,在露拍買了台二手車 (GIANT YUKON) 後,總算脫離每次都搭捷運租車來騎的日子了... 其實租車也沒什麼不好,不過租來的車子一來每次租的都不大一樣,二來都不能裝些慣用的配件... 騎起來總是不大順手,三來每次都得還車,行程多少會受限...

買了車後,可以騎的路線就廣多了.. 上上週搭捷運,試騎了 [捷運永寧站] <----> [大溪] 的路線,還蠻好騎的,風景也棒,這次就決定從家裡出發,拼一拼從大溪來回,順便突破單日破百..

image

圖一: 永寧 <--> 大溪 GPS Log, 來回約 50km。感謝小熊子贊助 GPS Logger :D

沒錯,男人就是這麼愛面子,自從上回有人留了話之後,有沒有單日破百,心裡總是覺的怪怪的... 加上沒趁機會累積一些當年勇,以後那有當年勇可以掛在嘴邊? 趁著這次小孩回娘家,天氣又正好,就來試一下...

image

[4.5km] 台北市淡水河三號水門 (延平門),我家到台北市河濱道,就走這裡最近了..

因為這次拼 100km, 路上老停下來照相大概就騎不完了,加上我的 G9 被帶回外婆家了,這次用的是我老爸的相機... 不大順手就沒照太多了... 中間這段就沒特別照了。所以有些地點的照片就直接用上回拍的... 上次是大晴天,這次是陰天,一看就知道了 :D

路線大概是這樣,到淡水河邊後,沿著淡水河右岸往南走,到華江橋牽車過橋,再往南沿著大漢溪右岸到新海橋,牽車上橋到左岸後一路騎到鶯歌..

 

image

[19.3km] 大漢溪左岸,快到城林橋了。

 

image

(上回的舊照片) 剛過城林橋..

 

image

[27km] 轉眼間已經到鶯歌了..

 

image

[28km] (上回舊照片) 鶯歌陶瓷博物館,經過了好幾次,可是都沒進去過 @_@

image

[28.3 km] 三鶯大橋下,鶯歌到大溪的自行車道 (2009/07 才通車)

image

過了三鶯大橋後,腳踏車道的風景就完全不一樣了 (Y)

image

過了個閘門,原來水都被關在這裡,難怪一路上大漢溪都沒什麼水...

image

真棒的風景,我喜歡這種有有山有水的 (Y)

 

image

離開溪邊,到大溪的這段路變成鄉間小逕,兩旁都是韭菜田及稻田...

image

[41.2km] 到大溪橋了 (這次沒照照片,拿上次的照片充數... :P)

看看碼錶,才 41.2km, 騎回家大概連 90 都不到,更別提破百了,真是失算 @@ 看看時間還夠,回程就繞去三峽老街逛逛好了。

 

image

雖然公司有好幾個人住三峽,不過每個都不知道那家有名的牛角麵包是那一家… 只會跟我講我上次買錯家了 XD...

看來還是 GOOGLE + 路人比較可靠,這次就找到了... 是在條不起眼的巷子裡,一家叫 [福美軒] 的麵包店.. 一堆人等著麵包出爐,排隊排到店外面... 足足排了一小時才買到 =_=,一個 20 元,每人限購 30 個...

image

在回程的路上拿了兩個出來吃,果然好吃 (Y)

 

image

柑園橋旁的XX公園 (抱歉,名字忘記了),有一片草地,前面的是蓮花池... 不過季節不對,看不到蓮花 @@ 那堆綠綠的是布袋蓮,可不是草地... 踩下去是會掉下去的

 

 

 

 

 

 

 

 

 

image

[85km] 華江橋上照的..

 

image

最後回到家,最後一個巷子口看了一下碼錶,98.5km…. @@

單日破百的行程怎麼可以敗在這最後這區區 1.5km ? 於是就繞了點路,去附近的腳踏車店打個氣,然後再回家... 正好 100.29 km! 哇哈哈,單日破百的成就達成!



7/21/2009 10:39:00 PM

拼了! 80公里長征... (關渡 - 鶯歌)

543 | 安德魯的當年勇

自從上次騎了一次關渡到八里之後,其實後來又去騎了幾次,發現也沒想像中的困難嘛,於是這次就計劃來拼長一點的路線... 關渡到鶯歌。會挑這路線,主要是上次騎到二重疏洪道時,有個路人問我:

"請問往鶯歌要怎麼騎?

我才發現,原來可以騎到鶯歌啊... 不過當下的反應是騎到那邊腿會斷掉吧? 不過用 google maps 看了一下,單程 35 公里 (加上迷路的一段路,來回應該有 80 了)... 之前近 30 公里都拼完了,騎到鶯歌好好休息,再騎回來應該沒什麼了不起吧? 加上上次那篇有人留話嗆了一句

"是男人就要挑戰百里長征啦"

....   愛面子的男人於是就很天真的出發了... =_=

很多人問我,為什麼每次都從關渡出發? 哈哈... 原因只有一個: 我很懶 :D,因為那邊捷運出站就有租車店,一輛還可以的變速登山車,當天租一次一台只要一百塊... 想想我一個月頂多騎一次,帶車子搭捷運票價也不止這個錢... 用租的比較方便,所以每次騎的路線就都挑從關渡為起點..

不過這次騎完,開始改變想法了... 哈哈,最後面再講。先來看看這次計劃的路線:

image

有 GOOGLE Maps 真是方便... 這次路線很簡單,前 1/3 是之前騎過的,之前是繞蘆洲三重一圈就回關渡了,這次會過重新橋,就改沿著大漢溪左岸,一路騎到鶯歌鎮...

出發前上網查了查,發現很多人騎過這段,最後是參考這個人的行程,看了才知道原來鶯歌有個 [阿婆壽司] ... 特色是便宜又不錯吃。雖然我沒有特別愛吃壽司,不過就把它當個目標吧,不然拼到鶯歌我也不知道要幹嘛 XD

算了算時間,一趟算 2.5 hr, 來回 5 hr, 加上一個小時休息吃東西,嗯,不用太早出門...

(啊,大家不要學... 你會後悔的 =_=)

2009/07/19 12:25

image

過程就不多說了 :P,到了關渡捷運站後,東摸西摸,騎上車開始動身後,過了大度路就先拍一張。我懶的拿紙筆出來計了,就拿起相機拍一拍了事 (時間可以從 EXIF 查,正好省掉記錄的動作),只可惜我的 G9 不支援 GPS ... 不然連地圖都不用標了 :D  Canon 你什麼時後要出內建 GPS 的相機...

路上的便利商店,買了兩罐冰釀綠茶,加上租車店老闆送的一罐水... 就上路了...

2009/07/19 13:37

image

悠哉的騎了一個小時,到了重新橋... 橋下有跳蚤市場,真熱鬧... 好多人 :D,不過騎著車不好人擠人,就沒進去逛了..

2009/07/19 13:43

image

雖然沒進去逛,不過也是要拍張照紀念。這邊的路標示實在不怎麼清楚,到了橋下就迷路了 :D 哈哈.. 在那邊摸了十幾分鐘,地圖拿出來,還看太陽在那邊認一認方向,硬著頭皮找了對的方向就騎下去... 沒有自行車道,只好走省道,自己識相一點靠邊邊騎...

好在沒騎多遠,就找到自行車道 =_=,就一路沿著大漢溪左岸的自行車道一路往西南騎...

雖然是 "自行車道",不過實際上也只是快速道路隔一條出來給自行車專用... 經費的關係吧 @@,沒關係,有就好,標示清楚一點就好。至少這邊不會騎一段就找不到路... 這是好處 :P

2009/07/19 14:02

image

騎到一半,才發現,原來新莊的 IKEA 就在路邊耶 :D

沒想到第一次來新莊的 IKEA 是騎著自行車來... 今天沒機會進去逛逛,只好拍張照紀念一下。

2009/07/19 14:15

image

騎到一半,看到一座天橋,上去後就可以在河提上面繼續騎,或是跨過河提到另一面的巷道裡。我也忘了看誰的文章介紹,他特別介紹了天橋兩側的鐵管... 我就糊理糊塗的跟著牽上去...

(大家不要學啊,這條路是錯的 XD 請不要上橋,繼續沿著自行車道騎就好 …)

image

都走錯了,不過走過總要留個記錄,還是貼一下好了,不要小看那兩根鐵管併起來的軌道,這樣子牽單車上橋還真的很輕鬆耶 (Y)(Y)(Y),不但很輕鬆就推上去了,鐵管中間還能卡著腳踏車的輪胎,不會亂跑,連車子都不用特別去控制它的方向,只要花點力氣把車往前推就好,真是聰明的設計 (Y)

2009/07/19 14:32

image

沿著河岸騎了一段之後,自行車道到這邊就結束了... 這邊下來把車扛上河提之後,就進入最後一段 [淡水-鶯歌] 10KM 的車道...

我是不知道為什麼這段叫 [淡水-鶯歌] 啦 @@,不曉得的人還以為真的 10 KM 就到了... 總之,這 10 KM 騎完就到鶯歌了,快到了快到了...

2009/07/19 14:49

image

遠遠看到這棟建築,還以為是什麼紀念館還是啥的... 原來是個抽水站...

2009/07/19 15:05

image

路過看到的,覺的很有意思就拍一下 :D

河邊放了一堆消波塊,竟然有人把它漆成這個樣子... 哈哈,真有創意,看起來就像一堆躲在草叢裡探出頭找獵物的迅猛龍...

2009/07/19 15:37

image

一路都沒看到路標,也不知道到底還多久 @@,最後怎麼覺的那一公里好遠... 騎到這邊早就沒力氣了,都慢慢騎... 總算撐到了鶯歌。看看地圖,其實離火車站沒多遠,不過懶的過去照相了 @@,動身去找阿婆壽司..

2009/07/19 16:12

image 

一開始問了一個媽媽,說往前騎,看到中正三路左轉就到了 (最後找到的地點是中正一路右轉)...

騎錯之後又問一個廿歲左右的小妹妹... 前面路口右轉就到了 (最後是左轉才對 =_=)

路人都報錯,我怎麼還找的到? @@,一切都要感謝全家便利商店外面整面牆上畫的地圖... 哈哈,最後是靠那張地圖找到的...

停下來吃了盒壽司 + 茶碗蒸,肚子餓了什麼都好吃... 50塊就吃飽了... 順手多帶了一盒回去... 水到這邊也都喝光光,去便利商店再補兩罐...

2009/07/19 19:00

image

image

回程的路線都一樣,就不再多介紹了,當然迷路過的地方就不會再走錯了 :P

路上河邊有好多人在玩搖控飛機,看了好想也去買來玩... :D  有個傢伙好強,控制的好靈活,其它的只是在亂飛而已。看他表演了好幾招,最後還表演了高級空戰技巧 - 英麥曼迴轉 (Immelman turn) ... 可惜他秀完這招就降落了 (降落的動作也很乾淨利絡... 其它的看起來都像要墜機的樣子 XD),不然我大概會在旁邊一直看吧...

其實從鶯歌要回程起,早就沒啥力氣了 :D,回來的速度就慢多了,騎回二重已經七點了... 當天天空雲還蠻厚的,不過我鐵齒沒搽防曬油,兩隻手被曬的好痛 Q_Q ...

不過也多虧這樣的天氣,當天有晚霞耶... 趕路歸趕路,有大景還是要停下來照一下 :D

不過沒背腳架,騎的很累手也拿不穩相機 @@,只好亂拍一通... 貼幾張還可以看的相片....

 

2009/07/19 19:27

image image

騎到關渡大橋,那段上坡早就沒力了,連試著騎上來的念頭都沒有.. 當大家都往前衝時我就很沒面子的下來用牽的... 哈哈。一路上一直很納悶,怎麼那些身材嬌小的長腿正妹,每個體力都這麼好 =_= 騎在前面跟都跟不上.... 車子比較好的關係嘛 @@

牽上橋後,休息一下,相機靠著欄干拍了幾張夜景,拍完就繼續往捷運站趕路了...

2009/07/19 19:50

image 

總算... 我頭一次這麼期待進到捷運站... 哈哈,總算可以坐下來了... 不然騎一天下來,屁股還真的會痛 @@

趕在 20:00 之前,總算拼完今天的行程了 :D  哈哈... 今天這樣騎下來,才發現裝備是很重要的... 平時騎兩三個小時那種就沒差了,要騎長程一點的真的要準備一下... 喝的一次就喝掉五罐小保特瓶... 延路都沒便利商店好補貨... 手套要戴 (不然手會握的很痛),排汗衣褲 (不然流一堆汗很難過),適合的背包 (一樣... 不然背很難過),還有電池夠力的 MP3 (我買了台 iPico, 還不錯 (Y),連續聽了七個小時... 只有最後一小段路沒得聽)...

最後,挑台自己的車好像也很重要... 哈哈... 這天我動過不只一次的念頭,很想到鶯歌就扛著腳踏車,搭火車回台北... 不過想到還要回到關渡去還車,就很懶... 還是騎回去好了。看看地圖,如果有自己的車的話,搭捷運到永寧,出來就樹林那一帶了,騎一個小時左右就可以到鶯歌了吧,省了不少路 :D  不過等到有買車再試吧...

這次相片放在 facebook 上,有興趣的人過去看看吧 :D

這次在鶯歌,又有路人在鶯歌問我 "請問大溪要怎麼去" @@

嗯,再說... 這一定是巧合... 這一定是巧合...



5/5/2009 3:12:38 AM

關渡騎單車

543 | 安德魯的當年勇

這次來寫點不一樣的,寫點休閒的吧。

 

上個月跟家裡大人跟兩個小孩,去了趟關渡騎腳踏車,騎完覺的那邊還不錯 :D,不過因為小孩狀況多,最後沒能騎到八里天就黑了,租來的腳踏車又沒燈,只好半路就折回來了。回家翻了一下地圖,發現只剩 1/3 不到的路程啊 @_@... 於是這次趁著大人帶著小孩回娘家,碰到難得的好天氣,就自己一個人帶著裝備出發去了 :D

計劃要騎的路線很簡單,一點都不困難... 就是從捷運 [關渡站],往關山公園,沿著淡水河岸的腳踏車道,騎過關渡大橋,到八里老街,再一路騎到十三行博物館。 不過計劃總是跟實際執行時不一樣 =_= ...  直接來寫流水帳吧... 照我騎的順序看下去...

這篇不是什麼專業的介紹,老實說我也是第一次騎而已,沒做什麼功課,想來參考的可能會失望吧 :D 只是單純的記下來自己留個紀錄而以。有興趣的請繼續往下看 :D

 

 

1. 捷運關渡站(15:09) ~ 八里渡船碼頭 (16:05), 共 8.4 KM

image 

GOOGLE MAPS 還蠻好用的,地圖跟路線都標的好好的 :D 上面的路線其實是車子走的,跟我真正騎的腳踏車道有點不同... 不過差不多啦,我就借用它的地圖標示一下。上面的每個點 (綠色的英文字母) 就是底下照片標的 ABC,各位可以對照著看。

第一段的路限很簡單,就是到了關渡捷運站後,租了腳踏車就上路了。從關渡捷運站出發,一路騎到八里渡船口而以.. 太陽還不小,但是天空雲也很多,還頗耽心會不會下雨... 因為這次出發帶了一堆配備 (腳架,相機,閃光燈,耳機...),就是沒帶傘 ...

 

A. 15:09,捷運關渡站 ( 0.0 km )
IMG_1086 (Canon PowerShot G9) (1024x768)

搭了半個多小時的捷運,到了關渡站,拍個照紀念一下。其實這裡沒什麼好照的,只是我也懶的拿紙筆出來記時間了... 哈哈,直接拍個照,回家可以看到照片,也看的到時間...  突然覺的應該買個有 GPS 的照相手機,這樣拍出來連座標都有了 [Y]

另外一個敗家目標是 MP3 隨身聽... 平常沒在聽,不過自己一個人邊騎車邊聽還真愜意,聽了一下午的陳綺貞... 結果聽到連手機都沒電了 =_=,看來用手機聽 MP3 不是長久之計,有空來物色一台...

好,列入敗家清單內... 出了捷運站就有租腳踏車的地方。雖然看其它網站,都是說要步行 15 分鐘到關渡宮那邊再租車,不過... 實在是懶的多走這 15 分鐘的路程了,在門口的租車店就租下去,到晚上八點,一次一百...

 

 

 

B. 15:16,大度路 (0.3km)
IMG_1090 (Canon PowerShot G9) (768x1024)

騎出巷子口,穿過橋下就到大度路口了,單純覺的這個景跟這個建築,好像在國外的感覺,就順手拍了一張...

 

 

 

C. 15:26,關渡棧橋碼頭 (1.1km)
IMG_1091 (Canon PowerShot G9) (1024x768) IMG_1092 (Canon PowerShot G9) (1024x768)

騎著腳踏車還蠻快的,穿過巷子,經過關渡醫院,到了關度宮,就到 [關渡棧橋碼頭] 了... 十分鐘不到的車程而已。在這邊看到天氣有點陰陰的,遠方已經看的到等等要過去的關渡大橋...

 

 

D. 15:32,關渡大橋上 (2.5km)
IMG_1095 (Canon PowerShot G9) (1024x768)

騎起來好像真的沒什麼挑戰 @_@,跟上次載著小孩完全不一樣,一方面親子車本來就不好騎,另一方面自己騎也比較自在一點,不到十分鐘已經在關渡大橋上了... 上橋是吃力了一點,人也多,只好下車用牽的... 這張照片就是在橋邊的步道拍的... 再往前左轉就在橋上了..

 

 

E. 15:56,已經到對岸了 (5.5km)
IMG_1100 (Canon PowerShot G9) (1024x768) IMG_1104 (Canon PowerShot G9) (1024x768)
過到對岸後,沿著河畔騎了廿分鐘左右,這個點不知道叫啥名字,會停下來只是喝帶來的冰釀綠茶,也剛好看到有個左右相反的 [八里左岸] 石碑,停下來照個相記錄一下... 不過有對姊妹 (應該是姊妹吧) 抱著狗一直在那邊照... 哈哈,心裡噓了半天還是不肯走... 還一直猛拍,一直拍,一直拍.... =_=,我又不想跟她們慢慢耗...,就讓妳們倆跟愛犬免費登上我的部落格一次吧 =_= ...

 

騎到這邊已經快到八里渡船頭了,沒有想像中的久嘛... 上次竟然騎不到 @_@

 

 

F. 16:05,八里渡船頭 (8.4km)
IMG_1106 (Canon PowerShot G9) (1024x768) IMG_1107 (Canon PowerShot G9) (1024x768)

再往前騎不到十分鐘,就... 到.. 了 @_@,從租到車到騎到八里,也才五十分鐘左右,扣掉停下來喝個水,照個相的時間... Hmm… 果然是很休閒的路線...

這裡一樣一堆人,趕不走 (我也沒那個膽.. 哈哈),就照進去了 :D

這邊是八里渡船頭,可以直接搭渡輪到淡水碼頭... 就是有顆大蓉樹那邊,還有一堆阿給小吃店的地方。

 

 

 

IMG_1109 (Canon PowerShot G9) (1024x768) IMG_1112 (Canon PowerShot G9) (1024x768)

照完相想繼續往下騎,一路直攻十三行博物館... 不過… My God! 那來這麼多人... 我最討厭人擠人了 :@,看到一堆人就很沒力... 何況牽著車跟本動彈不得... 就放棄繼續往前走的念頭了。

翻了翻地圖,另一邊有生態公園,看看時間跟騎的速度,應該還很夠吧 :D,就改變計劃往回走了...

 

 

 

 

 

 

2. 八里渡船碼頭 (16:05) ~ 疏洪生態公園 (17:20),共 8.0 KM

image

在決定不想擠過人群,往十三行博物館前進之後,看了看地圖,就決定往南騎到生態公園看看,這種地方人應該不會那麼多吧 :D

這一趟的路程也差不多一樣八公里,沒騎過,反正自己一個人就騎看看 :D

 

 

 

 

B. 16:26,八里左岸石碑 (11.3km)
IMG_1114 (Canon PowerShot G9) (768x1024) IMG_1119 (Canon PowerShot G9) (1024x768)

又回來這裡了! 不過,這次那對姊妹跟狗已經不在了 :D,沒人在拍照...  終於輪到我拍了 :D

石碑旁邊原來還有說明啊... 剛才都沒看到。本來想拿起相機自拍,不過技術不好,都拍不到後面,腳架也懶的扛出來... 就算了...,繼續往下一站!

 

 

 

C. 16:38 岸邊某個休息區 (12.3km)
IMG_1122 (Canon PowerShot G9) (1024x768)

回程的途中,有一小片沙灘,正好看的到關渡大橋,就照一下紀錄時間...

 

 

 

 

 

D. 16:50,虹橋廣場 (12.9km)
IMG_1124 (Canon PowerShot G9) (1024x768) IMG_1128 (Canon PowerShot G9) (1024x768) image

已經騎回來到關渡大橋橋下了,這邊剛好是從橋上下來的自行車道的地方,原來這 SQUARE 叫 "虹橋廣場" 啊... 拍個照。

不過這次沒有要過橋了,繼續沿著左岸往南騎...

 

 

E. 17:06 獅子頭長橋 (14.5km)
IMG_1133 (Canon PowerShot G9) (1024x768) IMG_1136 (Canon PowerShot G9) (768x1024) image

這裡是個半園型的橋... 不大會講,我剛好也沒照 @_@,抓張 GOOGLE MAP 的衛星照來看看... 那個像量角器的東西,就是獅子頭長橋啦...

這邊我只停下來拍照而已,沒多休息就往下一站去了...

 

 

 

17:12 觀音坑溪橋 (15.2km)
IMG_1140 (Canon PowerShot G9) (1024x768) IMG_1141 (Canon PowerShot G9) (768x1024) image

還蠻特別的一座橋,造型不錯就照了一下,其實橋很小一座... 就貼個照片跟 GOOGLE 衛星空照圖意思一下..

 

 

 

17:20 疏洪生態公園 (16.0km)
IMG_1146 (Canon PowerShot G9) (1024x768) image

不知不覺就騎到目的的了,果然沒很多人,一邊是河一邊是草地,在這邊坐著休息還蠻舒服的... 陪我騎了半天的腳踏車,終於有機會入鏡頭了 :D,叫不出來的牌子,不過還蠻好騎的 (Y),一次一百塊,我是覺的不貴啦,自己買一台少說四五千吧? 光是帶車子搭捷運就不只這一百塊了 =_=,還是當場用租的方便...

 

 

 

 

 

 

 

3. 疏洪生態公園 (17:20) ~ 捷運關渡站 (19:00),共 6.6 KM

image

看看時間也差不多了,還得趕回去接大人跟少爺公主回家 =_=,在疏洪生態公園休息一下就回頭了。一路上的風景跟景點都介紹過,就不多提了。在回程的路上,才發現 MP3 隨身聽的重要啊 @_@,一整天聽下來,也沒幾個小時 (3HR左右),我的手機在接到大人打來的電話之後,就... 沒... 電... 了,嘖嘖,windows mobile 的手機聽個 MP3 就這麼耗電...

不過這樣一路聽聽 MP3 還真是過癮,就是這樣我才想去買台來用... 有沒有推薦的? iPod 就不用推了,我沒這麼時尚 @_@...

 

 

17:42 關渡大橋 (八里 --> 淡水,19.0 km)
IMG_1156 (Canon PowerShot G9) (1024x768) IMG_1160 (Canon PowerShot G9) (1024x768) image

又回到關渡大橋了,這次是從左岸南方的步道上橋,第一張照片是還沒過橋前照的,第二張照片則是同一個地點,拍上來的地方,就是從畫面中間一路往右邊爬上來... 第三張是 GOOGLE MAPS 的空照圖,順手放上來...

這次因為時間的關係,不能待太晚,可惜沒等到晚一點天黑,沒機會拍到關渡大橋的夜景... @_@,看來腳架是白帶了...

 

 

18:14 關渡宮前的小吃 (21.5km)
IMG_1190 (Canon PowerShot G9) (1024x768) IMG_1196 (Canon PowerShot G9) image

繼續往回騎,騎到關渡宮前面的小市集吃東西... 其實當地我也不知道有什麼特別的小吃,就點了平常愛吃的就好... 這邊的鹹鴨蛋好像還蠻出名的,上回大人有買一些,不過這次就沒買了。另外離這裡不遠的淡水很有名的鐵蛋,這邊也有... 不過 $$ 幾乎便宜了一半 (9顆50),這個吃起來比鹹鴨蛋方便 (哈哈,不用剝殼),常常買了就當零嘴吃.. =_=

關渡宮就是 GOOGLE 衛星照中間的橘色建築,隔著馬路對面 (橘色屋頂),一個正方型的建築就是個小吃攤集中的場地... 無奈當天沒啥胃口,吃了一盤蚵仔煎 (五十元) ... 一份花生糖冰淇淋 (卅五元) ... 一顆鐵蛋 (帶了一包回家,九顆 50 元) 就... 飽了 =_=  不然還有其它的東西想吃一吃...

 

 

18:24 一堆怪名字的租車店
 IMG_1191 (Canon PowerShot G9) (768x1024)IMG_1192 (Canon PowerShot G9) (1024x768)IMG_1197 (Canon PowerShot G9) (1024x768)

除了蚵仔煎是坐在裡面的位子吃之外,其它我就在路邊的椅子買了就坐下來吃,路邊不是小吃就是租車店,發現他們店名還真有創意... 哈哈,害我邊吃邊笑..

第一家叫 "租八借",虧老闆想的出來...

第二家叫 "租羅記",老闆八成姓羅吧... =_=

這邊租一次只要 80,不過搭捷運的話,大概來回得多走個卅分鐘吧,算了,我是懶人,就讓另外的店家多賺廿塊錢吧...

第三家在旁邊一點,喵喵休閒車,人氣就差多了... 哈哈,招牌還在,不過店已經收起來了,底下是掛著店面出租的紅紙... 果然名字好不好記還是有差.. @_@

 

最後 19:00 整,回到捷運站前的租車店 (22.6km)

這裡就沒再拍照了 @_@    沒想到這樣很輕鬆的騎下來,也不知不覺騎了廿幾公里... 夠高速公路從台北開到桃園了吧? 這樣看起來好像還蠻遠的.. 哈哈。自己一個人騎,聽聽 MP3 就不無聊了,騎了多遠也沒什麼感覺,很適合來放鬆的。騎單車還真不錯,有風景可以看不會無聊 (平常騎機車或開車,都不能看風景 =_=),也 "好像" 有運動到,聽起來比較健康一點... :D

 

下次再看看天氣怎樣,試試別條路線... 看了看台北縣市自行車道的介紹 (這裡有地圖PDF檔下載),其它路線有往淡水 (不過淡水去過幾次,都像八里一樣人擠人 @_@),也有往關渡自然公園看水鳥的路線 (這路途比較短,不用一個小時就到了吧)....,另外還有往三重方向,可以繞一整圈三重/蘆洲... 還會經過三和夜市... 不知道有沒有好吃的小吃? 還有不知道會不會經過很紅的爆米花店? 哈哈,順便買個兩桶回來 :D

 

雖然自己騎蠻自在的,不過有人要跟團也接受報名啦 :D  看看下次有沒有機會拼完三重蘆洲這條自行車道...



10/1/2008 10:43:22 PM

得獎了 :D~~~

Microsoft.NET | 543 | C# | Threading | 小技巧 | 安德魯的當年勇 | 我的作品 | 技術隨筆 | 物件導向

 IMG_9142

 

雖然上禮拜就知道了,不過獎品還沒拿到,當然要忍一下再發表... 哈哈!

花了幾個晚上拼了猜數字的程式,運氣不錯,順利拼到冠軍了。除了寫程式,把心得貼到 BLOG 也花了不少時間.. 主要貼的這四篇:

  1. Thread Sync #1. 概念篇 - 如何化被動為主動?
  2. Thread Sync #2. 實作篇 - 互相等待的兩個執行緒
  3. [C#: yield return] #1. How It Work ?
  4. [C# yield return] #2. 另類的應用 - Thread Sync 替代方案

 

蠻有意思的比賽。雖然過去也參加過不少比賽,運氣不錯也騙到一些獎品...,不過這次倒是寫的最起勁,因為其它比賽都是 "廠商" 讚助,不是 Microsoft 就是 Cisco ... 都要想儘辦法把他們的技術發揮出來才能得獎,老實說寫起來跟工作差不多,總是要滿足那些 "市場" 的需求。

這次題目老實說很 "不實用",純粹是比 code 誰寫的又快又好而已,不過還蠻合我胃口的 :D。正好這次碰到誰呼叫誰這種結構上的問題,就是上面四篇文章一直在討論的 GameHost 為主還是 Player 為主的思考方式,解決這問題花的工夫還比較多。想到這兩套解決方式,我覺的收穫是蠻值得的,至少我多學到兩種不同的設計模式。

最後當然要感謝一下主辦人,下班專程騎車過來頒獎... 哈哈,獎品對我還蠻實用的,算是大獎一枚! 正好是我需要的東西,看來可以開始物色新硬碟,還有要準備來更新我的 SERVER 了 :D



10/1/2008 4:09:00 AM

該如何學好 "寫程式" #2. 為什麼 programmer 該學資料結構 ??

543 | C# | MSDN | 小技巧 | 安德魯的當年勇 | 我的作品 | 技術隨筆 | Microsoft.NET | [精選文章]

自從貼了上一篇 [該如何學好 "寫程式"] 一文,原本以為這種老生常談的內容沒什麼人會看,沒想到還有人給我回應.. :D 原來這種文章還是有市場的。接下來這篇,是延續上一篇,來談談要成為合格的 programmer, 我認為應該要俱備的 "內功" 是什麼。上篇我提到,我認知的 programmer,就是要有實作 (CODING) 的能力。要有能力把技術規格 (像是輸入格式,操作介面等等) 具體的寫成可以執行的程式碼。當然是要寫的又快又好,穩定不當機又沒 BUG ...。

 

在這個階段 (programmer),會一些具體的工具或是技術是必要的,但是它絕對不是主角。如何去運用你的工具才是關鍵。我認為 "資料結構" 就是能正確運用你的工具 (程式語言及函式庫) 最重要的知識。我常看到很多會一堆 "先進" 的技術,卻寫出很可笑的 code ... 。這種例子太多了,兩層迴圈擺錯順序,或是某些動作 *不小心* 擺到迴圈內,多花了好幾倍的時間在做冤枉事...。這種例子我通通把它規在基本功夫不好,或是常聽的邏輯觀念不佳。所以在上一篇我會提到,好的 programmer 至少能滿足我講的三個基本要求:

  1. 丟一付洗過的撲克牌給你 (不要多,黑桃1 ~ 13就好),你知道怎麼用 Bubble Sort / Quick Sort 的步驟把它排好嗎? 丟一個陣列,裡面隨便打幾個數字,你能寫程式把它由小到大排好印出來嗎?
  2. 假設記憶體夠大的話,你有辦法把一百萬筆通訊錄資料讀到記憶體內 (用什麼物件都隨你),然後還能用很快的速度找到你要的資料嗎? 不同的搜尋方式,你知道該用什麼樣的方式找才有效率嗎?
  3. 以台灣高速公路為題 (中山高、北二高、國道二號),你有辦法寫程式,讓使用者指定起點跟終點的交流道,然後替它找出建議的路線嗎? (把延路經過的交流到跟收費站列出來就好)

第一個只要你知道排序的方法,剩下的就是你有沒有本事把腦袋的想法寫成 CODE 而以。這個要求大部份的人都能過關,我就不多作解釋了。來看看第二個要求,它考驗的是你該用什麼樣的方式 "SEARCH" ?

我就以 C# 為例來說明這個問題該怎樣思考。以資訊系的 "資料結構" 這門課的角度來思考,你應該要找出個適合的資料結構 (Binary Tree, Heap, Linked List ... etc) 來存放這堆資料。不過資料結構這麼多種,你都要自己做嗎? .NET framework 已經在 System.Collection.Generic 這命名空間內提供了一堆好用的 Collection 給你用了,你該怎麼挑選才好? 課堂上老師不會教你實作的東西,而公司的前輩也不會教你這種基礎的東西,那你該怎麼把這兩者應用在一起?

就先從 (2) 的例子開始吧! 通訊錄最基本的要求,就是儲存的資料要能按照姓名/EMAIL/電話號碼排序。輸入名字後,要能很快的找到這個人完整的通訊錄。如果能像手機一樣,邊輸入名字就邊過濾名單,直到名字打完人就找到的話更好。在宣告了 class ContactData { ... } 類別來處理一筆資料後,下一步你會怎麼做?

 

ContactData 類別定義[copy code]
        public class ContactData        {            public string Name;            public string EmailAddress;            public string PhoneNumber;            public void OutputData(TextWriter writer)            {                writer.WriteLine("Name:\t{0}", this.Name);                writer.WriteLine("Email:\t{0}", this.EmailAddress);                writer.WriteLine("Phone:\t{0}", this.PhoneNumber);                writer.WriteLine();            }        }
   1:  public class ContactData
   2:  {
   3:      public string Name;
   4:      public string EmailAddress;
   5:      public string PhoneNumber;
   6:      public void OutputData(TextWriter writer)
   7:      {
   8:          writer.WriteLine("Name:\t{0}", this.Name);
   9:          writer.WriteLine("Email:\t{0}", this.EmailAddress);
  10:          writer.WriteLine("Phone:\t{0}", this.PhoneNumber);
  11:          writer.WriteLine();
  12:      }
  13:  }

 

開始來看看,有基本功夫的 programmer 跟一般 "熟 C# 熟 .NET" 的 programmer 差在那裡吧! 程式很簡單,先產生一百萬筆假資料,然後去找 A123456 這個人的資料,接著再找出手機號碼為 0928-1234 開頭的所有人資料。事後會分別計算花掉的時間跟程式佔用的記憶體大小。

 

1. 大概有 70% 的人,會選擇用 List<ContactData>,不為什麼,只因為他沒想到別的方法,或是直覺就覺的要這樣寫... 來看看這樣的 code:

用 List<ContactData> 寫的範例程式[copy code]
        private static void Sample1()        {            Stopwatch timer = new Stopwatch();            timer.Reset();            timer.Start();            // 產生假資料庫            List<ContactData> contacts = new List<ContactData>();            {                for (int index = 999999; index >= 0; index--)                {                    ContactData cd = new ContactData();                    cd.Name = string.Format("A{0:D6}", index);                    cd.EmailAddress = string.Format("{0:D6}@chicken-house.net", index);                    cd.PhoneNumber = string.Format("0928-{0:D6}", index);                    contacts.Add(cd);                }            }            Console.WriteLine("建資料花了: {0} ticks ({1} msec)", timer.ElapsedTicks, timer.ElapsedMilliseconds);            timer.Reset();            timer.Start();            {                // 搜尋 A123456 這個人的資料                ContactData data = null;                data = contacts.Find(delegate(ContactData x) { return x.Name == "A123456"; });                Console.WriteLine("搜尋 A123456 花了: {0} ticks ({1} msec)", timer.ElapsedTicks, timer.ElapsedMilliseconds);                //data.OutputData(Console.Out);            }            timer.Reset();            timer.Start();            {                // 列出電話號碼為 0928-1234* 開頭的使用者                foreach (ContactData match in contacts.FindAll(delegate(ContactData x) { return x.PhoneNumber.StartsWith("0928-1234"); }))                {                    //match.OutputData(Console.Out);                }                Console.WriteLine("搜尋 0928-1234* 資料花了: {0} ticks ({1} msec)", timer.ElapsedTicks, timer.ElapsedMilliseconds);            }            Console.WriteLine("共使用記憶體: {0}MB", Environment.WorkingSet / 1000000);        }
   1:  private static void Sample1()
   2:  {
   3:      Stopwatch timer = new Stopwatch();
   4:      timer.Reset();
   5:      timer.Start();
   6:      // 產生假資料庫
   7:      List<ContactData> contacts = new List<ContactData>();
   8:      {
   9:          for (int index = 999999; index >= 0; index--)
  10:          {
  11:              ContactData cd = new ContactData();
  12:              cd.Name = string.Format("A{0:D6}", index);
  13:              cd.EmailAddress = string.Format("{0:D6}@chicken-house.net", index);
  14:              cd.PhoneNumber = string.Format("0928-{0:D6}", index);
  15:              contacts.Add(cd);
  16:          }
  17:      }
  18:      Console.WriteLine("建資料花了: {0} ticks ({1} msec)", timer.ElapsedTicks, timer.ElapsedMilliseconds);
  19:      timer.Reset();
  20:      timer.Start();
  21:      {
  22:          // 搜尋 A123456 這個人的資料
  23:          ContactData data = null;
  24:          data = contacts.Find(delegate(ContactData x) { return x.Name == "A123456"; });
  25:          Console.WriteLine("搜尋 A123456 花了: {0} ticks ({1} msec)", timer.ElapsedTicks, timer.ElapsedMilliseconds);
  26:          //data.OutputData(Console.Out);
  27:      }
  28:      timer.Reset();
  29:      timer.Start();
  30:      {
  31:          // 列出電話號碼為 0928-1234* 開頭的使用者
  32:          foreach (ContactData match in contacts.FindAll(delegate(ContactData x) { return x.PhoneNumber.StartsWith("0928-1234"); }))
  33:          {
  34:              //match.OutputData(Console.Out);
  35:          }
  36:          Console.WriteLine("搜尋 0928-1234* 資料花了: {0} ticks ({1} msec)", timer.ElapsedTicks, timer.ElapsedMilliseconds);
  37:      }
  38:      Console.WriteLine("共使用記憶體: {0}MB", Environment.WorkingSet / 1000000);
  39:  }

 

憑良心說,寫的出這樣程式碼的人,已經算是高手了。因為這樣已經用到不少高級技巧,像是 delegate, anonums method, 還有知道 List<T>.Find( ) 怎麼用等等... 以下是他的執行結果:

image

 

 

2. 更進階一點的人 (另外 25%),也許會額外加上 Dictionary 當作索引,來改善 search A123456 這筆資料的效率...

加上 Dictionary 當作索引的 code[copy code]
            // 略            // 產生假資料庫            Dictionary<string, ContactData> name_index = new Dictionary<string, ContactData>();            List<ContactData> contacts = new List<ContactData>();            {                for (int index = 999999; index >= 0; index--)                {                    ContactData cd = new ContactData();                    cd.Name = string.Format("A{0:D6}", index);                    cd.EmailAddress = string.Format("{0:D6}@chicken-house.net", index);                    cd.PhoneNumber = string.Format("0928-{0:D6}", index);                    name_index.Add(cd.Name, cd);                    contacts.Add(cd);                }            }            Console.WriteLine("建資料花了: {0} ticks ({1} msec)", timer.ElapsedTicks, timer.ElapsedMilliseconds);            timer.Reset();            timer.Start();            {                // 搜尋 A123456 這個人的資料                ContactData data = name_index["A123456"];                //data = contacts.Find(delegate(ContactData x) { return x.Name == "A123456"; });                Console.WriteLine("搜尋 A123456 花了: {0} ticks ({1} msec)", timer.ElapsedTicks, timer.ElapsedMilliseconds);                //data.OutputData(Console.Out);            }           // 略
   1:   // 略
   2:   // 產生假資料庫
   3:   Dictionary<string, ContactData> name_index = new Dictionary<string, ContactData>();
   4:   List<ContactData> contacts = new List<ContactData>();
   5:   {
   6:       for (int index = 999999; index >= 0; index--)
   7:       {
   8:           ContactData cd = new ContactData();
   9:           cd.Name = string.Format("A{0:D6}", index);
  10:           cd.EmailAddress = string.Format("{0:D6}@chicken-house.net", index);
  11:           cd.PhoneNumber = string.Format("0928-{0:D6}", index);
  12:           name_index.Add(cd.Name, cd);
  13:           contacts.Add(cd);
  14:       }
  15:   }
  16:   Console.WriteLine("建資料花了: {0} ticks ({1} msec)", timer.ElapsedTicks, timer.ElapsedMilliseconds);
  17:   timer.Reset();
  18:   timer.Start();
  19:   {
  20:       // 搜尋 A123456 這個人的資料
  21:       ContactData data = name_index["A123456"];
  22:       //data = contacts.Find(delegate(ContactData x) { return x.Name == "A123456"; });
  23:       Console.WriteLine("搜尋 A123456 花了: {0} ticks ({1} msec)", timer.ElapsedTicks, timer.ElapsedMilliseconds);
  24:       //data.OutputData(Console.Out);
  25:   }
  26:  // 略

 

會寫到這樣,也算強者了。不但對 C# 夠熟,也有用 Collection 物件來當作索引的觀念。程式碼只有兩行不同,一個是多宣告了 Dictionary 物件 (第3行),另一個是搜尋的地方 (第21行)。來看看執行結果:

image

 

果然有效,建資料從 5151ms 變成 5843ms, 記憶體從 288MB 變成 340MB,不過 search(A123456) 卻快的嚇人, 0ms... 破錶了!

 

但是這樣的 CODE 老實說只能算是及格而以,因為它沒有挑對 Collection 來用。怎麼說? 我的理由有這幾個:

  1. List<T> 的搜尋效能不好
  2. 沒能滿足用多種排序方式的要求 (需要時要當場執行 List<T>.Sort( ))

如果這是某個 Mail Client 內的 CODE,產品經理一定會問:

"如果資料從一百萬筆,變成一億筆,程式的表現會是什麼情況?"

 

有沒有基本功夫,這裡開始有差別了。唸過資料結構的都知道有個叫 "時間複雜度" (time complexity) 的東西,用 O(n) 表示。O(n) 代表花費的時間會跟資料比數成線性的成長。100倍的資料大概就要花上100倍的時間.. 如果是 O(n^2) 的演算法,則 100 倍的資料就會花上 10000 倍的時間。

MSDN 專業的地方就在這裡。Microsoft 真的很細心的在每一個 Collection 物件的說明文件上,都會標上 time complexity。有唸書有保佑,瞄到那行字我的問題就都解決掉了。先來看看 List<T> 的行為:

 

List<T>.Add(T item)

If Count is less than Capacity, this method is an O(1) operation. If the capacity needs to be increased to accommodate the new element, this method becomes an O(n) operation, where n is Count.

 

List<T>.FindAll(Predicate<T> match)

This method performs a linear search; therefore, this method is an O(n) operation, where n is Count.

 

再來看看 Dictionary<TKey, TValue> 的行為:

Dictionary<TKey, TValue>.Add(TKey key, TValue value)

If Count is less than the capacity, this method approaches an O(1) operation. If the capacity must be increased to accommodate the new element, this method becomes an O(n) operation, where n is Count.

 

Dictionary<TKey, TValue>.Item[TKey key] {get; set;}

Getting or setting the value of this property approaches an O(1) operation.

 

好,答案出來了。當資料變成一百倍時,List.Add 是 O(1), 所以每加一筆資料的時間不會越來越久 (safe). 但是搜尋時間是 O(n), 意思是現在找 A123456 要花 60ms, 未來有一億筆就要花 60x100=6000ms=6sec, 找 0928-1234* 則要花 240x100=24000ms=24sec... 以這樣的成長速度,記憶體還沒用完,你的程式就會慢到受不了了。有沒有其它的解決辦法?

 

換成 Dictionary 就酷多了,搜尋時間是 O(1), 代表不管你有幾筆,搜尋的時間都差不多。為什麼? MSDN 說的很清楚...

http://msdn.microsoft.com/en-us/library/xfhwa508.aspx

The Dictionary<(Of <(TKey, TValue>)>) generic class provides a mapping from a set of keys to a set of values. Each addition to the dictionary consists of a value and its associated key. Retrieving a value by using its key is very fast, close to O(1), because the Dictionary<(Of <(TKey, TValue>)>) class is implemented as a hash table.

 

什麼是 HashTable? 又是一個好例子,唸過資料結構的都知道吧? 我就不多說了,請看 wiki:

http://en.wikipedia.org/wiki/Hashtable

 

一樣是看 MSDN 文件,有沒有唸過資料結構,到這裡就差這麼多了。體會到學校教的東西真的有用了嗎? 這個例子還沒完,再看下去。

 

事實上,以上的實作方式都不合格。LIST 效能不好,Dictionary 拿來作索引有個致命的缺點:

它的 KEY 不能重複!!!

是的,對應到資料庫的話,它就好像是個 unique key 一樣。拿來當 NAME 的索引還沒問題,拿來當其它欄位的索引就糟糕了,別說效能問題,連用都不能用。

另外,針對排序的問題也是無解,這是 HashTable 的特性,要照順序排,就要另請高明。

 

事實上,以上的實作方式都不合格。List 搜尋的效能不算好,而 Dictionary 也只能處理 exact match 的狀況,同時也無法處理需要排序的問題。

 

唸過書的再想想,這時該怎麼辦?

標準解法是分別照這幾個欄位排序,然後用 Binary Search. 這才是正解。因為排序好的資料就像一般資料庫的 index, 可以讓你很容易的 order by, 同時又能讓你很快的找到你要的資料,甚至是列出某一段範圍的資料都沒問題。

不過寫成程式要怎麼作? MSDN 就在手上嘛,System.Collection.Generic 就把它當購物網站,逛一逛... 看有沒有其它合用的。

 

不錯,又找到兩個: SortedListSortedDictionary,還是一樣,那一個比較合適? MSDN 都寫的很清楚,足夠你判斷了,前題是資料結構教的幾個基本觀念 (像是前面講的 Hash Table, Time Complexity 等) 人家寫出來你要看的懂,看的懂就知道該挑那一個。

 

至於挑選的過程我就不多說了。我最後決定用 SortedList, 列一下這個 Collection 的特性:

SortedList.Add( )

This method is an O(n) operation for unsorted data, where n is Count. It is an O(log n) operation if the new element is added at the end of the list. If insertion causes a resize, the operation is O(n).

 

新增一筆需要的時間是 O(n), 唯一特例是加在最後面,而且沒引發 resize 的動作,就是 O(log n)。至於排序? 通通是 O(1),因為在 Add() 把資料加進來時就排好序了,所以 Add() 花的 O(log n) 就是在排序。要照順序印資料或找資料,完全不費吹灰之力,拿來印就是了。不過比較可惜的是,SortedList 並沒有提供 BinarySearch,因此要找 "0928-1234*" 這樣的資料要辛苦點,自己用 BinarySearch 的邏輯,簡單寫一下吧。如果前面的關卡都過了,這應該不難吧?

改用 SortedList 最大的缺點就是載入資料時會比較慢,不過其它在程式的處理上,還有效能都更貼近這個題目的需求。來看看程式碼,這次我用了兩個 SortedList,分別代表替 name 及 phone number 作排序:

 

改用 SortedList<> 的範例[copy code]
        private static void Sample3()        {            Stopwatch timer = new Stopwatch();            timer.Reset();            timer.Start();            // 產生假資料庫            SortedList<string, ContactData> name_index = new SortedList<string, ContactData>();            SortedList<string, ContactData> phone_index = new SortedList<string, ContactData>();            {                for (int index = 0; index < 1000000; index++)                {                    ContactData cd = new ContactData();                    cd.Name = string.Format("A{0:D6}", index);                    cd.EmailAddress = string.Format("{0:D6}@chicken-house.net", index);                    cd.PhoneNumber = string.Format("0928-{0:D6}", index);                    name_index.Add(cd.Name, cd);                    phone_index.Add(cd.PhoneNumber, cd);                }            }            Console.WriteLine("建資料花了: {0} ticks ({1} msec)", timer.ElapsedTicks, timer.ElapsedMilliseconds);            timer.Reset();            timer.Start();            {                // 搜尋 A123456 這個人的資料                ContactData data = name_index["A123456"];                Console.WriteLine("搜尋 A123456 花了: {0} ticks ({1} msec)", timer.ElapsedTicks, timer.ElapsedMilliseconds);                //data.OutputData(Console.Out);            }            timer.Reset();            timer.Start();            {                // 列出電話號碼為 0928-1234* 開頭的使用者                for (int pos = BinarySearch<string, ContactData>(phone_index, "0928-1234");                    pos < BinarySearch<string, ContactData>(phone_index, "0928-1235");                    pos++)                {                    //phone_index.Values[pos].OutputData(Console.Out);                }                Console.WriteLine("搜尋 0928-1234* 資料花了: {0} ticks ({1} msec)", timer.ElapsedTicks, timer.ElapsedMilliseconds);            }            Console.WriteLine("共使用記憶體: {0}MB", Environment.WorkingSet / 1000000);        }        private static int BinarySearch<TKey, TValue>(SortedList<TKey, TValue> index, TKey key)        {            return BinarySearch<TKey, TValue>(index, key, 0, index.Count - 1);        }        private static int BinarySearch<TKey, TValue>(SortedList<TKey, TValue> index, TKey key, int start, int end)        {            if (start == end) return end;            int pos = (start + end) / 2;            int compareResult = index.Comparer.Compare(key, index.Keys[pos]);            if (compareResult == 0)            {                return pos;            }            else if (compareResult > 0)            {                return BinarySearch<TKey, TValue>(index, key, pos + 1, end);            }            else            {                return BinarySearch<TKey, TValue>(index, key, start, pos - 1);            }        }
   1:  private static void Sample3()
   2:  {
   3:      Stopwatch timer = new Stopwatch();
   4:      timer.Reset();
   5:      timer.Start();
   6:      // 產生假資料庫
   7:      SortedList<string, ContactData> name_index = new SortedList<string, ContactData>();
   8:      SortedList<string, ContactData> phone_index = new SortedList<string, ContactData>();
   9:      {
  10:          for (int index = 0; index < 1000000; index++)
  11:          {
  12:              ContactData cd = new ContactData();
  13:              cd.Name = string.Format("A{0:D6}", index);
  14:              cd.EmailAddress = string.Format("{0:D6}@chicken-house.net", index);
  15:              cd.PhoneNumber = string.Format("0928-{0:D6}", index);
  16:              name_index.Add(cd.Name, cd);
  17:              phone_index.Add(cd.PhoneNumber, cd);
  18:          }
  19:      }
  20:      Console.WriteLine("建資料花了: {0} ticks ({1} msec)", timer.ElapsedTicks, timer.ElapsedMilliseconds);
  21:      timer.Reset();
  22:      timer.Start();
  23:      {
  24:          // 搜尋 A123456 這個人的資料
  25:          ContactData data = name_index["A123456"];
  26:          Console.WriteLine("搜尋 A123456 花了: {0} ticks ({1} msec)", timer.ElapsedTicks, timer.ElapsedMilliseconds);
  27:          //data.OutputData(Console.Out);
  28:      }
  29:      timer.Reset();
  30:      timer.Start();
  31:      {
  32:          // 列出電話號碼為 0928-1234* 開頭的使用者
  33:          for (int pos = BinarySearch<string, ContactData>(phone_index, "0928-1234");
  34:              pos < BinarySearch<string, ContactData>(phone_index, "0928-1235");
  35:              pos++)
  36:          {
  37:              //phone_index.Values[pos].OutputData(Console.Out);
  38:          }
  39:          Console.WriteLine("搜尋 0928-1234* 資料花了: {0} ticks ({1} msec)", timer.ElapsedTicks, timer.ElapsedMilliseconds);
  40:      }
  41:      Console.WriteLine("共使用記憶體: {0}MB", Environment.WorkingSet / 1000000);
  42:  }
  43:  private static int BinarySearch<TKey, TValue>(SortedList<TKey, TValue> index, TKey key)
  44:  {
  45:      return BinarySearch<TKey, TValue>(index, key, 0, index.Count - 1);
  46:  }
  47:  private static int BinarySearch<TKey, TValue>(SortedList<TKey, TValue> index, TKey key, int start, int end)
  48:  {
  49:      if (start == end) return end;
  50:      int pos = (start + end) / 2;
  51:      int compareResult = index.Comparer.Compare(key, index.Keys[pos]);
  52:      if (compareResult == 0)
  53:      {
  54:          return pos;
  55:      }
  56:      else if (compareResult > 0)
  57:      {
  58:          return BinarySearch<TKey, TValue>(index, key, pos + 1, end);
  59:      }
  60:      else
  61:      {
  62:          return BinarySearch<TKey, TValue>(index, key, start, pos - 1);
  63:      }
  64:  }

 

執行結果:

image

 

至於前面產品經理問的問題,各位就試著自己到 MSDN 找看看答案吧! 比較過之後,你就會瞭解為什麼我會挑選 SortedList .. 我只挑 SEARCH 時間來看,List 的搜尋是 O(n), 而 SortedList 的搜尋是排序過的資料作 BinarySearch, 找找書就知道是 O(log n), 分別來比較一下:

當 N 等於 1000000 時:

List: 3131861 ticks
SortedList: 39294 ticks (快 80 倍)

 

推算一下,N 放大為 100 倍 (100000000) 時:

List: 3131861 x 100000000 / 1000000 = 313186100 ticks
SortedList: 39294 x log 100000000 / log 1000000 = 52392 ticks (快 5978 倍)

 

看到了嗎? 換個 Collection 物件,對於 Search 這個動作,一百萬筆資料時差了 80 倍,當資料成長一百倍 (100000000 筆) 時,搜尋速度差異爆增為近 6000 倍! 這就是資料結構或是演算法的差異,這樣的差異已經大到其它地方最佳化怎麼作都補不回來的地步,唯一一個關鍵就是要用對演算法!

   

終於打完這篇了。沒想到前一篇寫一堆老生常談的話,這次又變成一堆 sample code 了。不過我的目的就是讓各位瞭解,基礎一定要顧好啊,不然寫程式是一定會碰到瓶頸的。這次從很簡單的需求,帶到資料結構的觀念,再帶到 MSDN 裡面特別標記的資訊...。看完後應該不會再有人說學校教的東西沒用了吧?

 

有網友問過我有沒有推薦什麼書? 很抱歉,我也只看過課本而以 ... 哈哈,這些純粹是出來工作後,無意間還想到要去翻翻課本得來的經驗。其實這種例子很多,過去我常貼的 multi-thread 的文章也是很多這樣的例子,只不過課本從資料結構換成作業系統了,這個主題才寫到 1-2, 後面還有, 有什麼看法或心得就請留在回應給我吧! 如果能支持一下旁邊的讚助商的話也算是種鼓厲啦.. 敬請期待下一篇..

--

調查一下,有人看這篇之前就知道 SortedList 嗎? 留個話給我吧,我很好奇這種東西有多少人會去用... :D



12/11/2007 7:14:00 PM

Canon Raw Codec + WPF #1, WPF Image Codec, Metadata

Microsoft.NET | Threading | WPF | 我的作品 | 安德魯的當年勇 | 技術隨筆

託 Canon G9 的福, 這一個月來的空閒時間都在研究 Windows Presentation Foundation 裡的 Image Codec 相關事項. 幹嘛買個相機還這麼辛苦? 因為原本算計好的計劃, 就差這一環啊... 雖然老早就有換機的計劃, 為什麼龜了那麼久 G9 一出來就跑去買? 除了之前講了一堆相機本身的考量之外, 剩下的關鍵因素是 RAW support. 因為:

  1. Canon G9 "又" 開始支援 RAW file
  2. Canon 正好搭配 WPF (windows presentation foundation), 發表了它專屬的 RAW file codec.
  3. WPF 裡提供了許多 JPEG 無法帶來的好處, 我打的如易算盤是: 不管未來是什麼東西取代了 JPEG, 留著 RAW 一定沒錯, 因此支援 RAW 就大大加了不少分.

RAW support 在我看來是必要的. 照片可不能等十年後再搭時光機回來照, 而現有的 JPEG 又已經是老古董的規格了, 未來是一定會有取而代之的新規格. 會是什麼我不曉得, 不過留著 RAW 準沒錯. 只要 Canon 沒倒, 未來一定有辦法把 RAW 轉成新的通用格式, 而不用經過 JPEG 的折損...

未來看起來很美好, 不過當 G9 入手後, 事情沒有想像的順利, 有了 WPF + Codec, 我自己必需寫些小程式來簡化未來例行的相片歸檔動作. Codec 只要去 Canon 下載就有, WPF 則是新東西, 得自己先研究一番... 搭配 WPF, Microsoft 也推出了新的圖型檔格式: HD Photo. 它的一堆好處我就不多說了, M$ 網站多的是. 我在意的是, HD Photo 提供的新功能, 包括廣大的色域等, 也會對應的在 image codec 裡提供. 因此如果 RAW file 本身就包含這些資訊的話, 透過 codec 讀出來就不會有資訊的折損, 存成 JPEG 就沒有這些好處了.

實作第一步, 當然是先研究 WPF 關於 Image 物件的基本處理. 嗯, 果然是跟之前的 GDI / GDI+ 不一樣. 感覺起來 GDI 典型 application 就是像 "小畫家" 這類的 AP, 而 WPF 典型的 application 就是像 flash 這類的, 裡面的物件已經變成圖型來源, 套用各種 transform, 層層處理套上去後得到的結果才是你看到的東西, 大概就類似 photoshop 的 layer 那樣的東西.

講了那麼多, 其實我也只是要用到 codec 來讀取 canon raw file, 把圖檔縮成我要的像素, 存成指定的 jpeg 檔而以... 我就以這個例子貼一段 sample code

   1:  BitmapDecoder source = BitmapDecoder.Create(
   2:      new Uri(@"C:\IMG_0001.CR2"),
   3:      BitmapCreateOptions.DelayCreation,
   4:      BitmapCacheOption.None);
   5:  JpegBitmapEncoder target = new JpegBitmapEncoder(); 
   6:   
   7:  target.Frames.Add(BitmapFrame.Create(
   8:      new TransformedBitmap(source.Frames[0], new ScaleTransform(0.3, 0.3)),
   9:      source.Frames[0].Thumbnail,
  10:      metadata,
  11:      null)); 
  12:  target.QualityLevel = 80; 
  13:   
  14:  FileStream trgs = File.OpenWrite(@"C:\IMG_0001.JPG");
  15:  target.Save(trgs);
  16:  trgs.Close(); 

而過去也寫過 GDI+ 版本的, 那個我就不貼了. 老實說 code 也不會太多, 不過寫起來就覺的是兩種不同層次的思考邏輯. 也隨著這次機會花了點心思研究 System.Windows.Media.Imaging 裡的東西, 就開始想把舊的程式都翻一翻.. 包括了之前的歸檔程式, 及另一個批次縮圖的工具. 後面有機會再貼.

圖檔基本內容處理掉之後, 接下來就是 exif. 我很在意這些圖檔隱藏的資訊, 也不知道為什麼, 哈哈. 上面的 code 轉完是沒有半點 EXIF 的, 沒轉過來感覺就像少了什麼... 無奈在處理 metadata 時又碰到了一些小 trouble ...

 

花了一些時間搞定了 metadata, 不過又碰到不同檔案格式之間的 metadata 轉換問題. WPF 的 Image Codec 已經把 metadata 的讀寫方式給 "抽像化" 了, 所有圖檔的 metadata 都用一樣的方式讀寫. 它採用的是類似 xpath 的 metadata query 來指明目標是那個 metadata, 然後再用 GetQuery( ) 來讀值, 或是用 SetQuery( ) 把值寫進去. 現在碰到的問題是 Canon Raw 的 query 跟 JPEG exif 用的對應不起來. 我也不知道怎麼解, google 找幾個 sample 對照著比一比, 摸黑試了幾種對應方式, 竟然看起來還好像猜中了, 就不管先用下去. 我簡單的把整理的對照表貼一下...

/ifd/{ushort=256} --> /app1/ifd/exif/{ushort=256}
/ifd/{ushort=257} --> /app1/ifd/exif/{ushort=257}
...

請不要問我這是啥意思, 我真的也搞不懂, 看了 w3c 一些 spec, 真是天書... 大概只知道 metadata 有幾種規範, exif, xmp, ifd 等等. 而 ushort=256 大概就是指整個 block 裡, 第 256 bytes 位置的 ushort 的值就是這筆資料存放的地方等等. 我是拿幾張照片轉換後對照著看, 看起來對就將就著用了. 最後是用程式跑了一份看起來可用的對照表, 存成 xml 檔, 丟在自己寫的 library project 裡, 當作 embedded resource. 供未來轉檔的動作時拿出來用. library 包裝好之後用起來像這樣:

 

   1:  ImageUtil.SaveToJPEG(
   2:      @"c:\IMG_0001.CR2",
   3:      @"c:\IMG_0001.JPG",
   4:      800,
   5:      800,
   6:      75);

 

 

弄到現在, 總算把最基本的動作: 轉檔 (含 exif) 給搞定了. 總算有足夠的資訊把 library 給弄好. 不過馬上就碰到第二個大問題... "效能". 這部份就留著下一篇吧.



2/3/2007 3:59:00 AM

新手機真是讚 (y) - II

543 | 安德魯的當年勇 | 敗家

既然要勸敗, 就要勸的徹底一點, 不過我當然寫不出那種從開箱就一堆照片, 然後啥 ooxx 很有質感, 接著再拿一堆手機出來疊疊樂的文章, 咳咳..

發現買 Dopod C720W 這支手機的人似乎不多, 討論也是少到可憐, 不過這款手機倒是有不少優點, 我事前除了做足了功課之外, 還專程去抓了 M$ 的 Smartphone SDK + Device Emulator, 把我要的功能都試了好幾次, 確認跟我想的一樣才跑去買... 我就挑幾個合我胃口的優點講一講:

 

 

Dopod 內建了幾個軟體, 很鳥的就不講了, 不過 "語音命令" 這個軟體還真不錯 (Y), 軟體不大, 語音也不用事先錄好, 直接講就可以了, 像上面的例子, 我只要 hotkey 按住, 對著手機講 "打電話到叭樂雞行動電話", 接著講 "確定", 我的電話號碼就撥出去了..., 講 "查詢叭樂雞", 我的通訊錄就跳到我那一筆...

 

 

這個軟體 ( pTravelAlarm ) 從我上一支 smartphone 就開使用了, 沒什麼特別的功能, 就是簡單好用而以. Microsoft 內建的鬧鐘很沒有誠意, 連兩百塊的電子錶都比它強 :@, 我的要求也不多, 能用 midi / mp3 / wma 當鈴聲, 能夠讓我訂兩三組, 上班 & 周末各在不同時間響就好了...

不過說也奇外, 這麼簡單的功能還真找不到滿意的, 這套本來在 WM5 smartphone 有小 bug, 就是 kb 鎖住後鬧鐘又響, 事後會按不到解鎖的鈕, 變成你的手機就解不開了 :@, 解法只剩自己打通電話進來, 或是拔掉電池這兩招... 本來想試試其它軟體都不合用, 剛好一月中更新的版本解掉這個鳥問題, 就繼續用這套習慣的小鬧鐘...

 

 

Screen Saver + Auto Lock

 

手機剛買來前兩天, 就為了這件事很苦惱, 一堆鍵, 旁邊還有觸控條, 帶在身上光是要拿出來就會不小心按到一堆鈕... 收起來時忘了 lock keyboard 就麻煩了, 用慣折蓋機對這點還真不能適應... 果然 smartphone 能灌軟體是個大優點, 什麼要求都找的到軟體可以用...

這個是德國人寫的小程式, 選項多到會眼花, 我只挑兩個功能... 一個是像電腦的 screen saver 一樣, 隔幾秒不動就自動 lock keyboard .. 另一個就是它可以自定 screen saver 的畫面, 設定方式挺抽像的, 不過彈性很大就是, 這段 video 就是我調的, 還是弄個類比時鐘比較順眼... :D

底下那排 icon 會隨時跟著 smartphone 的狀態作變化, 像是有沒有開 wifi, bluetooth, 電池有沒有電, 有沒有漏接來電等等... 還挺方便的, 另外有顯示溫度 ??? 不過那個溫度怎麼看都不準, 不知道資訊是打那來的?

 

 

 

上一篇提到多普達的鳥客服... 手機買來手癢就給它 hard reset, 然後要去抓軟體回來裝, 網站連個影都沒有 :@, 對, 即使到現在還是沒有. 本來有內建一個字典也沒得用了, 只好去挖這套之前在 8390 上面用的字典出來裝...

這年頭, 連手機都有 clear type ....  =.=, 左邊的是開 clear type 的效果, 右邊的沒有... 其它就沒啥特別了, 就是一般的字典而以.. 我裝了英英跟英漢兩套, 果然有 qwerty keyboard, 查起來真順手, 哈哈...

 

 

 

Dopod 最近的手機都內建一套 RSS reader, 本來我是想...

 "手機內建 rss 能幹嘛? 我又不會一天到晚上網"

不過就算每天只同步一次, 這個軟體還真不錯用, RSS 剛好可以把這些消息大綱很快的上網抓下來, 然後就帶在身上無聊拿出來看一下... 如果 theme 換成多普達的, 在首頁還會隨機輪播 RSS 的文章標題...  現在到處都有 RSS 可以訂閱, 每天簡單更新一下就幾十則新聞自動跑進來... 遠比用 mail 或是 mobile favorites 方便太多了... 沒上網時就有東西可以看, 當然有上網時更棒... 可以直接連到網頁去看全文...

不過用手機上網幹嘛? wifi 又不是那麼方便, 我也沒有 3G / GPRS 吃到飽... 這時 bluetooth 的好處就來了, 我的電腦插著 bt dongle, 剛好我在床上躺著還可以收到訊號, 臨時想連一下就不用爬起來... 哈哈, 所以現在我都窩在床上看 RSS ..

補幾張圖... 左邊是看 RSS 內容的畫面, 右邊是點了 "線上閱讀" 後開 IE 的畫面.. 怎麼上網的? 本來用 wifi 後來覺的太麻煩, 我現在都改用 bluetooth + activesync 了...

 

 

 

 

 

 

PaPaGo G10 就沒啥好講了, 之前只耽心 320x240 這種解析度會不會有問題? 試了結果是一切正常 :D, 補幾張圖應付一下.. 哈哈..

 

 

 

這個軟體叫 Smart Journal, 是裝在 PC 上, 不是裝在手機上... 只要每次 ActiveSync 一啟動, 它就會跟著跑起來, 會把 SmartPhone 上面的通話記錄都倒到 outlook 的日誌 (Y), 我的 outlook 就會有所有的通聯記錄了, 咳咳.. 它會記錄來電 or 去電, 沒接到的也會記, 如果通訓錄對的到, 就會直接秀名字, 也會有通話時間... 這個小軟體太棒了, 有用 windows mobile 的一定要去裝一下...

 

 

最後要講的是象牙筷子... 咳咳, 果然敗家會忍不住一直敗下去... 為了這支手機總共多敗了三樣雞絲...

  1. 那個買到假貨的 bluetooth headset 就不要提了, 雖然 NTD480 而以, 賣家還有一萬多個好評... 反正我本來就只是要買個便宜堪用的... 沒想到現在連 3C 產品也有假貨, 外型很像, 包裝很像, 不過很多小地方不同, 模具也差, 不過抓到的 device id 還一模一樣, 對岸的功力果然不是蓋的... 之前看壹周刊, 對岸連雞蛋都有假的... (沒錯, 假蛋打開也有蛋白蛋黃, 拿去煎還會熟...  -_-)

  2. Kingston 1GB micro SD, 含 SD 轉卡, 還有一個 microSD 的 USB 讀卡機 (不用轉卡), 含稅, 總共是 NTD 550 + 50 ... 同樣的東西 512mb 要 300 + 50, 想都沒想就標了 1G 的... 不過, 塞不完啊 :'(   努力塞了半天還剩 7xx mb ...

  3.  最後一個, 用了三個禮拜的 USB bluetooth dongle, 受不了了, 凸一塊在旁邊, notebook 很難收到包包裡, 拔掉的話要連線又要抓半天, ThinkPad 專用的 BMDC-300 貴就算了, 沒內建天線, 還要破解 BIOS 啥的... 受不了就去敗了這張 PCMCIA 的 bluetooth adapter, 不過也不便宜, 夠買兩支同等級 USB 的 bluetooth dongle 了.. 這張的收訊範圍是 200m (號稱), 舊的那支我剛好躺在床上用手機就是極限了, 翻個身就收不到, 換了這張怎麼滾都有訊號... 哈哈!! 不過美中不足的是, 買來插上電腦才發現, 這張 PCMCIA 其實裡面是 USB host chip + USB bluetooth chip... 所以插上電腦是先找到 VIA USB Host Controller... :@, ouch !!! 腦袋馬上響起一句話.. "VIA sucks..." 嘖嘖, 好在用起來沒啥大問題, 就睜一隻眼閉一隻眼吧...

  4. GPS bluetooth receiver... 這個還沒敗, 不過我只打算買個便宜堪用的就好, 哈哈... 這個其實不算買手機後才要買的東西, 我本來就有一個了, 只不過之前有一次拿錯變壓器, 充壞了, 還可以用, 不過關不掉, 電池一放就猛閃燈, 也不會進入省電模式, 猛閃個半小時就沒電去...

好了, 別再撐了, 快去敗吧... 對, 就是說你們兩個 :D



11/19/2006 2:41:08 AM

Ya!! Community.Chicken-House.Net 滿兩週年了 :D

543 | 安德魯的當年勇

嗯, 這個站架起來的第一篇是 2004/11/19 貼的, 剛好滿兩年 :D

當然再之前我自己寫的那個 forum.net 不算在內啦, 被唸到臭頭的網頁 :~~ 這個只是拿來練功用, 想當年這個 forum 也替我賺了不少獎品 :D, 現在小皮愛玩的 xbox (看 2004/01/05 那篇), 路癡用的 PDA 導航, 老婆大人每天當電視看的 HP 15" LCD.. 還有老爸用的 scanner / printer (2004/10/13那篇)... 都是這程式賺來的.. (H)

如果把 forum.net 也算進去的話, 可以追溯到這篇... 2003/06/08...

如果把更早的留言板 (當年只用 xml 檔當 storage, 沒有用 database) 算進去.. 哇靠, 這篇更早,  2002/07/01 ...

當時還沒花錢買 chicken-house.net 這個 domain name, 用的還是免費的 chicken.27south.com .. 南方廿七號養雞場... -_-

再往前算? BBS ... 嗯, 好, 不提當年勇了 [H]

謝謝大家的支持, chicken-house.net 會繼續開下去的 :D



11/15/2006 1:40:27 AM

IQ test ...

543 | 安德魯的當年勇

同事給的 IQ test 網站: http://web.tickle.com/tests/uiq/index-pop.jsp?sid=...

只有 124 分 :'(

共有 40 題, 英文的題目... 看來敗在英文太爛, 真糟糕, 語言能力只有 40% ~ 50% 的水準 :~~~

測完有詳細的 report, 分四個領域個別分析你的能力.. 貼一小段我的測試報告上來:

 

 

 

 

 


Congratulations, Andrew!
Your IQ score is 124

This number is based on a scientific formula that compares how many questions you answered correctly on the Classic IQ Test relative to others.
Your Intellectual Type is Precision Processor. This means you're exceptionally good at discovering quick solutions to problems, especially ones that involve math or logic. You're also resourceful and able to think on your feet. And that's just some of what we know about you from your test results.

 

 

 

Mathematical Intelligence

Your Mathematical Percentile
90th percentile

You scored in the 90th percentile on the mathematical intelligence scale.This means that you scored higher than 80% - 90% of people who took the test and that 10% - 20% scored higher than you did. The scale above illustrates this visually.
Your mathematical intelligence score represents your combined ability to reason and calculate. You scored relatively high, which means you're probably the one your friends look to when splitting the lunch bill or calculating your waitresses' tip. You may or may not be known as a math whiz, but number crunching might come a little easier to you than it does others.

 

 

 

Visual-Spatial Intelligence

Your Visual-Spatial Percentile
70th percentile

You scored in the 70th percentile on the visual-spatial intelligence scale.
This means that you scored higher than 60% - 70% of people who took the test and that 30% - 40% scored higher than you did. The scale above illustrates this visually.
The visual-spatial component of intelligence measures your ability to extract a visual pattern and from that envision what should come next in a sequence. Your score was relatively high, which could mean that you're the one navigating the map when you're on an outing with friends. You have, in some capacity, an ability to think in pictures. Maybe this strength comes out in subtle ways, like how you play chess or form metaphors.

 

 

 

Linguistic Intelligence

Your Linguistic Percentile
50th percentile

You scored in the 50th percentile on the linguistic intelligence scale.
This means that you scored higher than 40% - 50% of people who took the test and that 50% - 60% scored higher than you did. The scale above illustrates this visually.
Linguistic abilities include reading, writing and communicating with words. Tickle's test measures knowledge of vocabulary, ease in completing word analogies and the ability to think critically about a statement based on its semantic structure. Your score was relatively low, which could just mean that you convey ideas through other means than verbally. Or maybe it just means you find other ways to spend your time than doing crossword puzzles.

 

 

 

Logical Intelligence

Your Logical Percentile
100th percentile

You scored in the 100th percentile on the logical intelligence scale.
This means that you scored higher than 90% - 100% of people who took the test and that 0% - 10% scored higher than you did. The scale above illustrates this visually.
Tickle's logical intelligence questions assess your ability to think things through. The questions determine the extent to which you use reasoning and logic to determine the best solution to a problem. Your logic score was relatively high, which could mean that when the car breaks down, your friends look to you to help figure out not only what's wrong, but how to fix it and how you're going to get to the next gas station.



1/4/2005 1:22:00 AM

93' 美研聘書

543 | 安德魯的當年勇



交大美術社每年暑假都會舉辦的美術研習營... 大二那年被推去做社長這個苦差事, 到了暑假只好扛下總召集人的任務, 咳咳... 歷年來 (我辦的那屆好像是 19th or 20th 屆), 每年暑假都有六個社團會一起辦營隊, 美研, 土研, 羅服, 另外三個就忘光了, 全部加起來就是竹湖夏令營, 聲勢倒還挺浩大的...

不過沒想到還有聘書耶 :D, 雖然現在找工作派不上啥用處, 不過看起來也挺爽的.. (H) 哈哈..



12/20/2004 2:08:00 AM

高二: 物理/化學 全校第一名

543 | 安德魯的當年勇



12/20/2004 2:08:00 AM

高三: 物理 全校第一名

安德魯的當年勇




12/20/2004 2:06:00 AM

高一: 物理/總成績 全校第一名

安德魯的當年勇




12/20/2004 1:33:00 AM

小六: 香包比賽

安德魯的當年勇



( 哈, 挖出之前研究所時代做的網頁, 就直接貼上來了 ... )

小學時後沒得過什麼獎, 唯一一次上台 (升旗時上台領的喔....) 是在 國小六年級吧! 領的是「香包製做比賽」的獎, 第二名咧!!不過拿回家又是 被笑一噸... 哈哈哈!!說什麼男孩子這麼會拿針線做什麼??到現在過年回南 部, 那些老喜歡開我玩笑的姑姑們還老是記得這件事..老問我現在是不是還 常拿針線啊? 衣服褲子破了是不是都自己縫啊? 嘖嘖嘖...






精選文章

RUN! PC 文章及範例下載
2010/07. 結合檔案及資料庫的交易處理
2010/05. TxF讓檔案系統也能達到交易控制
2010/04. 生產者 vs 消費者 - 執行緒的供需問題
2008/11. 生產線模式的多執行緒應用
2008/09. 用ThreadPool發揮CPU運算能力
2008/06. SEMAPHORE在ASP.NET的應用
2008/04. 以ASP.NET開發同步WEB應用程式

如何學好 "寫程式" 系列
#1. 該如何學好 "寫程式" ??
#2. 為什麼 programmer 該學資料結構 ??
#3. 進階應用 - 資料結構 + 問題分析
#4. 你的程式夠 "可靠" 嗎?

#5. 善用 TRACE / ASSERT

安德魯是誰?

Andrew Wu | Create Your Badge

我喜歡鑽研物件導向、軟體工程及作業系統等相關技術。我會在這裡發表我的研究心得,也當作我自己的學習筆記。


Recent comments

Comment RSS