<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" xml:lang="en" xml:base="http://fcamel.twbbs.org/wp-atom.php">
	<title type="text">fcamel's blog</title>
	<subtitle type="text">自強不息的駱駝有草吃</subtitle>

	<updated>2009-06-18T16:00:30Z</updated>
	<generator uri="http://wordpress.org/" version="2.7">WordPress</generator>

	<link rel="alternate" type="text/html" href="http://fcamel.twbbs.org" />
	<id>http://fcamel.twbbs.org/feed/atom/</id>
	

			<link rel="self" href="http://feeds.feedburner.com/fcamel" type="application/atom+xml" /><entry>
		<author>
			<name>fcamel</name>
					</author>
		<title type="html"><![CDATA[為什麼要寫 unit test？為什麼要先寫測試？]]></title>
		<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/fcamel/~3/kfK5j4orxHo/" />
		<id>http://fcamel.twbbs.org/?p=849</id>
		<updated>2009-06-13T01:02:40Z</updated>
		<published>2009-06-12T18:00:22Z</published>
		<category scheme="http://fcamel.twbbs.org" term="All" /><category scheme="http://fcamel.twbbs.org" term="Programming" /><category scheme="http://fcamel.twbbs.org" term="Software Engineering" /><category scheme="http://fcamel.twbbs.org" term="Testing" />		<summary type="html"><![CDATA[這篇說明寫 unit test的好處，以及為何先寫 unit test 再實作，比實作完再補 unit test 更好。這裡先列出摘要：

測試碼可以簡省手動測試的時間，但有錯時無法告訴我們錯誤的源頭在那。
unit test 可以告訴我們錯誤的源頭在那，可是 unit test 有時間成本和維護成本。寫過多 unit test 反而有害。
TDD 藉由先寫測試避開 unit test 的成本問題，並帶來其它好處。


為什麼要寫測試碼？
吃燒餅那有不掉芝麻，寫程式自然要驗證程式是否正確。想像平時我們如何驗證程式？大概就是寫一寫，執行看看，看看輸出符不符合預期，換些不同輸入，再看看結果，有錯再改，改完再重跑．．．。測試碼可以協助我們自動化驗證，省時省力。
也許有人會懷疑寫測試碼不划算，功能常改變，到時測試碼又要重改。這個問題視情況有不同種解法，比方用較「便宜」的方式寫測試，像是準備好輸出入檔，用 shell script 執行，配合 diff 看輸出是否一致。或是用 scripting language (如 Python) 寫簡單測試。而更根本的解法，個人認為是配合 TDD，待後面說明。
手動執行測試，執行個三十年，每次花的時間還是一樣，也許還會因為手酸變慢。但寫測試碼則相反，我們會愈寫愈快，愈寫功能愈好。
為什麼要寫 unit test (單元測試)？
首先介紹不同級別的測試碼，由小到大依序為：

unit test：測試對象為單一函式。
integration test：測試對象為數個單元函式的複合體。我沒特別區分這個和 unit test [*1]，以下兩者一起用 unit test 表示。
system test：測試整個系統。
acceptance test：客戶驗收用的測試。將使用情節或使用需求轉成 acceptance test，藉以確認產品滿足客戶要求，方便起見，以下以 system test 統稱。


備註 *1：在談論 mock 時，unit test 和 [...]]]></summary>
		<content type="html" xml:base="http://fcamel.twbbs.org/archives/2009/06/13/849/">&lt;p&gt;這篇說明寫 &lt;a href="http://en.wikipedia.org/wiki/Unit_testing" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;unit test&lt;/a&gt;的好處，以及為何先寫 &lt;a href="http://en.wikipedia.org/wiki/Unit_testing" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;unit test&lt;/a&gt; 再實作，比實作完再補 &lt;a href="http://en.wikipedia.org/wiki/Unit_testing" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;unit test&lt;/a&gt; 更好。這裡先列出摘要：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;測試碼可以簡省手動測試的時間，但有錯時無法告訴我們錯誤的源頭在那。&lt;/li&gt;
&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Unit_testing" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;unit test&lt;/a&gt; 可以告訴我們錯誤的源頭在那，可是 &lt;a href="http://en.wikipedia.org/wiki/Unit_testing" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;unit test&lt;/a&gt; 有時間成本和維護成本。寫過多 &lt;a href="http://en.wikipedia.org/wiki/Unit_testing" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;unit test&lt;/a&gt; 反而有害。&lt;/li&gt;
&lt;li&gt;&lt;a href="/archives/2009/03/09/747/"&gt;TDD&lt;/a&gt; 藉由先寫測試避開 &lt;a href="http://en.wikipedia.org/wiki/Unit_testing" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;unit test&lt;/a&gt; 的成本問題，並帶來其它好處。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span id="more-849"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;為什麼要寫測試碼？&lt;/h4&gt;
&lt;p&gt;吃燒餅那有不掉芝麻，寫程式自然要驗證程式是否正確。想像平時我們如何驗證程式？大概就是寫一寫，執行看看，看看輸出符不符合預期，換些不同輸入，再看看結果，有錯再改，改完再重跑．．．。&lt;strong&gt;測試碼可以協助我們自動化驗證&lt;/strong&gt;，省時省力。&lt;/p&gt;
&lt;p&gt;也許有人會懷疑寫測試碼不划算，功能常改變，到時測試碼又要重改。這個問題視情況有不同種解法，比方用較「便宜」的方式寫測試，像是準備好輸出入檔，用 shell script 執行，配合 diff 看輸出是否一致。或是用 scripting language (如 Python) 寫簡單測試。而更根本的解法，個人認為是配合 &lt;a href="/archives/2009/03/09/747/"&gt;TDD&lt;/a&gt;，待後面說明。&lt;/p&gt;
&lt;p&gt;手動執行測試，執行個三十年，每次花的時間還是一樣，也許還會因為手酸變慢。但寫測試碼則相反，我們會愈寫愈快，愈寫功能愈好。&lt;/p&gt;
&lt;h4&gt;為什麼要寫 unit test (單元測試)？&lt;/h4&gt;
&lt;p&gt;首先介紹不同級別的測試碼，由小到大依序為：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;unit test：測試對象為單一函式。&lt;/li&gt;
&lt;li&gt;integration test：測試對象為數個單元函式的複合體。我沒特別區分這個和 &lt;a href="http://en.wikipedia.org/wiki/Unit_testing" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;unit test&lt;/a&gt; [*1]，以下兩者一起用 &lt;a href="http://en.wikipedia.org/wiki/Unit_testing" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;unit test&lt;/a&gt; 表示。&lt;/li&gt;
&lt;li&gt;system test：測試整個系統。&lt;/li&gt;
&lt;li&gt;acceptance test：客戶驗收用的測試。將使用情節或使用需求轉成 acceptance test，藉以確認產品滿足客戶要求，方便起見，以下以 system test 統稱。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;p&gt;
備註 *1：在談論 mock 時，unit test 和 integration test 的差異較明顯，本文不談這個議題，總得讓大家有意願寫測試，再來談如何寫測試才有意義，你說對吧。
&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;以測試的觀點來看，system test 的 test case 夠多，system test 應該能涵蓋到大範圍的產品碼，如此一來也達成我們一開始提的「驗證」目的。從實務觀點來看，寫愈多測試碼，負擔愈多，規格變動時要改的東西也多。更何況 &lt;a href="http://en.wikipedia.org/wiki/Unit_testing" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;unit test&lt;/a&gt; 著眼點更細，變更實作方式也有可能得改 &lt;a href="http://en.wikipedia.org/wiki/Unit_testing" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;unit test&lt;/a&gt;。如此一來，為什麼要寫 &lt;a href="http://en.wikipedia.org/wiki/Unit_testing" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;unit test&lt;/a&gt;？&lt;/p&gt;
&lt;p&gt;首先，&lt;a href="http://en.wikipedia.org/wiki/Unit_testing" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;unit test&lt;/a&gt; 著眼點比 system test 小，也意味著&lt;strong&gt;當測試結果不對時，unit test 可以指出更明確的問題點，&lt;/strong&gt;而 system test 只能粗略地和我們說輸出不對，接著我們得比對正確和錯誤的輸出，開始推測 bug 在那，並用 debugger 設中斷，或在程式內輸出訊息一步步找出問題源頭。相信大家都能明白除錯的痛苦。而 &lt;a href="http://en.wikipedia.org/wiki/Unit_testing" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;unit test&lt;/a&gt; 可以協助我們釐清那些程式是對的，那些是錯的，將問題範圍縮小。若 &lt;a href="http://en.wikipedia.org/wiki/Unit_testing" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;unit test&lt;/a&gt; 寫得好，幾乎用不到 debugger 和輸出訊息，光看那個 &lt;a href="http://en.wikipedia.org/wiki/Unit_testing" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;unit test&lt;/a&gt; 錯誤，就知道 bug 在那。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;除了幫自己省時間外，unit test 也可幫助別人維護和理解程式。&lt;/strong&gt;維護的人不如自己熟悉，難免不小心改爛程式，&lt;a href="http://en.wikipedia.org/wiki/Unit_testing" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;unit test&lt;/a&gt; 可以減少這類問題，也讓原作者安心給其他人修改。新手要閱讀程式時，&lt;a href="http://en.wikipedia.org/wiki/Unit_testing" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;unit test&lt;/a&gt; 可看成是各函式的使用範例，相信大家都同意讀例子比讀全部程式碼來得容易吧。好的範例有時比註解還有用。&lt;/p&gt;
&lt;h4&gt;為什麼要先寫測試？&lt;/h4&gt;
&lt;p&gt;好吧，若讀者大人耐著性子看到這裡，想必已對測試碼已有些心動，也認為寫 &lt;a href="http://en.wikipedia.org/wiki/Unit_testing" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;unit test&lt;/a&gt; 有那麼幾分道理，那先寫測試和後寫測試有什麼差別？若後來寫的程式不管用，需求變更，先寫測試不是搬磚砸自己的腳，自找麻煩嗎？事實上正好相反。&lt;/p&gt;
&lt;p&gt;我們得先問自己，為什麼程式會不管用？為什麼需求會變更？去除客戶或主管找麻煩的因素外，一部份原因為思慮不週，寫的功能不夠貼近目標。&lt;strong&gt;如同產品一般，沒試用過我們不會明白產品的缺點在那，函式也是一樣。&lt;/strong&gt;先寫測試碼就是先想像要如何使用即將要寫的函式，&lt;strong&gt;在寫測試碼的同時，我們同時也在設計函式。&lt;/strong&gt;有時會發現難以測試，而想出更簡潔的介面。當我們能輕鬆寫出測試碼時，也意味著目標函式易於使用，之後才方便重用。&lt;/p&gt;
&lt;p&gt;再者，先寫測試，相當於先列需求，規範中的輸入為何？預期輸出為何？可能有什麼例外情況？於是，我們接著寫出的程式，一定是有用的。我們不會忽然想說「要不要加個 foo()，之後大概會用到吧。」&lt;strong&gt;先寫測試，確保之後寫出的每一行產品碼都是有意義的。&lt;/strong&gt;同時，我們寫的測試碼也是有意義的，若事後再補測試，不明白加入一個 &lt;a href="http://en.wikipedia.org/wiki/Unit_testing" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;unit test&lt;/a&gt; 有何幫助，有時會多寫不必要的 unit test，如同前面提過，測試碼不是萬靈丹，它也同時是負擔，需求變更時，有時也得一併改測試碼。&lt;/p&gt;
&lt;p&gt;除幫助設計、減少寫冗碼的機會外，&lt;strong&gt;先寫測試還方便我們之後進行重構。&lt;/strong&gt;沒有測試碼的重構是很危險的，而沒重構的程式碼也是很危險的 [*2]，就像在底層不穩的地基上不斷加蓋偷工減料的高樓一般。先寫測試的好處還有一點，&lt;strong&gt;早寫早享受，&lt;/strong&gt;先寫測試，就能先自動化驗證，省去所有手動驗證。&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;
備註 *2：為什麼要重構？這是一個值得獨立討論的議題，主因為減少隱藏的 bug 和強化程式碼的可讀性，並更容易重用，以及增加新功能。詳見 &lt;a href="http://martinfowler.com" onclick="javascript:pageTracker._trackPageview('/outbound/article/martinfowler.com');"&gt;Martin Fowler&lt;/a&gt; 寫的 &lt;a href="http://www.books.com.tw/exep/prod/booksfile.php?item=0010411649" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.books.com.tw');"&gt;《重構：改善既有程式的設計》&lt;/a&gt;
&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;綜合以上所言，先寫測試花費的時間，可以攤算在設計時間，以及減少寫無用程式、減少手動驗證、縮短除錯的時間，所以&lt;strong&gt;先寫測試不見得會增加額外時間，我們只是把投入不划算事上的時間，先拿來寫測試。&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;特別加贈：何時該寫新的測試碼？&lt;/h4&gt;
&lt;p&gt;雖然寫測試好處多多，也別卯起勁來狂寫，最後卻覺得投資沒回本，浪費過多時間寫測試。遵照 &lt;a href="/archives/2009/03/09/747/"&gt;TDD&lt;/a&gt; 的原則，先依規格寫測試，別浪費時間寫也許有用的測試。&lt;/p&gt;
&lt;p&gt;那麼，何時才是跳出 &lt;a href="/archives/2009/03/09/747/"&gt;TDD&lt;/a&gt; 迴圈，另外加寫測試的時機呢？依我個人經驗，我發現以下三個情況，都是寫新測試碼的好時機：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;在程式裡輸出訊息找錯誤&lt;/strong&gt;：與其花時間寫 print、看輸出訊息、再回頭砍掉 print，不如寫個 unit test，之後能持續受惠。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;執行 debugger&lt;/strong&gt;：同上，你還是得花時間設中斷，一步步看訊息，不如寫個 unit test，之後能持續受惠。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不知錯誤在那，該如何進行下一步&lt;/strong&gt;：這表示一次貪心實作太多功能，把目標縮小，一步步補 &lt;a href="http://en.wikipedia.org/wiki/Unit_testing" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;unit test&lt;/a&gt; 吧。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;另外，&lt;strong&gt;在解 bug 時，先寫個 &lt;a href="http://en.wikipedia.org/wiki/Unit_testing" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;unit test&lt;/a&gt; 重製問題，再開始除錯。&lt;/strong&gt;如此一來可確保自己明白成因，之後同樣的 bug 也不會再出現。&lt;/p&gt;
&lt;h4&gt;超級加贈：若 TDD 這麼好用，為啥它不遍及？&lt;/h4&gt;
&lt;p&gt;施主，這個問題得問你自己啊，快來加入推廣 &lt;a href="/archives/2009/03/09/747/"&gt;TDD&lt;/a&gt; 的行列吧！&lt;/p&gt;
&lt;p&gt;說正經的，雖然上面將寫 &lt;a href="http://en.wikipedia.org/wiki/Unit_testing" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;unit test&lt;/a&gt; 和 &lt;a href="/archives/2009/03/09/747/"&gt;TDD&lt;/a&gt; 說得如此美好，寫 &lt;a href="http://en.wikipedia.org/wiki/Unit_testing" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;unit test&lt;/a&gt; 不是簡單的事。若寫得不好，就會有「改變實作，也得改變測試」的副作用，或是測試執行過久，不方便反覆執行，或是寫出無用的測試。如同學習 design pattern 一般，unit test 也有 design pattern，對沒接觸過寫測試碼的人來說，相當於要花「兩倍」時間學習。加上前面提到大家可能有的誤解，使得 &lt;a href="/archives/2009/03/09/747/"&gt;TDD&lt;/a&gt; 難以落實。&lt;/p&gt;
&lt;p&gt;另一個可能是，若你的組織同時有兩批人馬對同一產品進行開發和除錯，程式碼分成兩個 &lt;a href="http://www.ericsink.com/scm/scm_branches.html" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.ericsink.com');"&gt;branch&lt;/a&gt;：一個加入新功能，一個除錯。重構會造成程式碼難以合併，決策者因而選擇不頻繁重構。並且，這類組織可能有龐大的人力另外進行測試 (可看成是 system / acceptance test)，而誤以為不需要 unit test。&lt;/p&gt;
&lt;p&gt;然而，長遠來看，第一個理由顯然是偷懶，只是把現在的問題延後到日後再解決。趁早把除錯的時間省下來，投資到 &lt;a href="/archives/2009/03/09/747/"&gt;TDD&lt;/a&gt; 這積優股吧。第二個情況可能是公司考量，專業分工使得公司容易找人和培訓，我沒在這類公司工作的經驗，不知能如何對應。&lt;/p&gt;
&lt;p&gt;詳細的 &lt;a href="/archives/2009/03/09/747/"&gt;TDD&lt;/a&gt; 風險，可參照 &lt;a href="http://stackoverflow.com/questions/64333/what-is-the-downside-to-test-driven-development/64696#64696" onclick="javascript:pageTracker._trackPageview('/outbound/article/stackoverflow.com');"&gt;What is the downside to Test Driven Development?&lt;/a&gt;。結論是團隊要有熱情學一堆新技術，或是有個經驗老道的開發者帶才適合用 &lt;a href="/archives/2009/03/09/747/"&gt;TDD&lt;/a&gt;。不過，我個人的觀點是，現實世界的選擇不是非一即零，不是所有程式都能 &lt;a href="/archives/2009/03/09/747/"&gt;TDD&lt;/a&gt;，若成本過高用 &lt;a href="/archives/2009/03/09/747/"&gt;TDD&lt;/a&gt; 不划算，那就暫時別花時間學相關技巧，挑軟柿子吃。至少，有用 &lt;a href="/archives/2009/03/09/747/"&gt;TDD&lt;/a&gt; 的部份就能享受到 &lt;a href="/archives/2009/03/09/747/"&gt;TDD&lt;/a&gt; 帶來的好處。別因為一些可能想見的問題，而全盤否定使用 &lt;a href="/archives/2009/03/09/747/"&gt;TDD&lt;/a&gt;。&lt;/p&gt;
&lt;h4&gt;我對 TDD 充滿熱血，請問如何入門？&lt;/h4&gt;
&lt;p&gt;好吧，這個標題是我寫來自 high 的。若有興趣的話，可以參考 &lt;a href="http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata" onclick="javascript:pageTracker._trackPageview('/outbound/article/butunclebob.com');"&gt;The Bowling Game Kata&lt;/a&gt; 的&lt;a href="http://butunclebob.com/files/downloads/Bowling%20Game%20Kata.ppt" onclick="javascript:pageTracker._trackPageview('/outbound/article/butunclebob.com');"&gt;投影片&lt;/a&gt;，由設計保齡球計分程式的小例子，一步步展示 &lt;a href="/archives/2009/03/09/747/"&gt;TDD&lt;/a&gt; 進行的過程，相信跟著例子走一次必能有所啟發，親身體驗 &lt;a href="/archives/2009/03/09/747/"&gt;TDD&lt;/a&gt; 如何帶來新設計。若能自己先寫一遍，再來看投影片，感受會更深。或是參閱我之前寫的介紹文：&lt;a href="/archives/2009/03/09/747/"&gt;《TDD 推廣：背景知識和簡介》&lt;/a&gt;以及&lt;a href="/archives/2009/04/19/784/"&gt;《最近用 Python + TDD 心得（與 Java 做對照）》&lt;/a&gt;。&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/fcamel/~4/kfK5j4orxHo" height="1" width="1"/&gt;</content>
		<link rel="replies" type="text/html" href="http://fcamel.twbbs.org/archives/2009/06/13/849/#comments" thr:count="0" />
		<link rel="replies" type="application/atom+xml" href="http://fcamel.twbbs.org/archives/2009/06/13/849/feed/atom/" thr:count="0" />
		<thr:total>0</thr:total>
	<feedburner:origLink>http://fcamel.twbbs.org/archives/2009/06/13/849/</feedburner:origLink></entry>
		<entry>
		<author>
			<name>fcamel</name>
					</author>
		<title type="html"><![CDATA[以有涯逐無涯，殆矣。]]></title>
		<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/fcamel/~3/AAmWs2l3ivM/" />
		<id>http://fcamel.twbbs.org/archives/2009/05/11/842/</id>
		<updated>2009-05-11T15:14:05Z</updated>
		<published>2009-05-11T15:05:42Z</published>
		<category scheme="http://fcamel.twbbs.org" term="All" /><category scheme="http://fcamel.twbbs.org" term="Life" /><category scheme="http://fcamel.twbbs.org" term="格言錦句" />		<summary type="html"><![CDATA[近幾個月學習狀況不錯，學習效率好也能持久，但總覺得愈學愈多，愈學愈累，偶爾會有種茫然的挫折感。忽然想到莊子的這句：

吾生也有涯，而知也無涯。以有涯逐無涯，殆矣。

原本我曲解了這段話，還振奮了一下，想說要更認真地追求知識。忽然，我發現莊子的本意反而是點出恰到好處的生活方式，於是我的世界改變了。

體悟到這點後，我決定晚上十點後停止電腦科學相關的學習，改看看動畫、或翻翻閒書，或偶而像這樣寫寫 Blog 也不壞。待之後多存點錢，買了電鋼琴還可以來個「彈琴復長嘯」。不知為何，我覺得自己可以用更自在的方式追求更深的知識，讓生活過得趨於平衡。
忽然，又有兩年半前相似的體悟，到外邊小晃了一圈，我似乎走回正確的路了。即使不知道方向，不知道終點何在，我明白自己正踏在想走的路。
題外話，看到現在學習程式語言和機器學習的進展，不禁懷疑過去為何期望這麼久，卻遲遲沒有行動。才想起以前還有許多想做的事，像是增加電腦科學的廣度認知、寫 3D 遊戲、參加各式比賽、做研究寫論文等。許多事若沒有親身嘗試，只會念念不忘，難以打消念頭。經過前幾年的嘗試，才能像現在般沉澱心靈，專心在最有興趣的幾個點上。
]]></summary>
		<content type="html" xml:base="http://fcamel.twbbs.org/archives/2009/05/11/842/">&lt;p&gt;近幾個月學習狀況不錯，學習效率好也能持久，但總覺得愈學愈多，愈學愈累，偶爾會有種茫然的挫折感。忽然想到莊子的這句：&lt;br /&gt;
&lt;blockquote&gt;
吾生也有涯，而知也無涯。以有涯逐無涯，殆矣。
&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;原本我曲解了這段話，還振奮了一下，想說要更認真地追求知識。忽然，我發現莊子的本意反而是點出恰到好處的生活方式，於是我的世界改變了。&lt;/p&gt;
&lt;p&gt;&lt;span id="more-842"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;體悟到這點後，我決定晚上十點後停止電腦科學相關的學習，改看看動畫、或翻翻閒書，或偶而像這樣寫寫 Blog 也不壞。待之後多存點錢，買了電鋼琴還可以來個&lt;a href="http://back-big5.zhengjian.org/articles/2007/9/27/48562.html" onclick="javascript:pageTracker._trackPageview('/outbound/article/back-big5.zhengjian.org');"&gt;「彈琴復長嘯」&lt;/a&gt;。不知為何，我覺得自己可以用更自在的方式追求更深的知識，讓生活過得趨於平衡。&lt;/p&gt;
&lt;p&gt;忽然，又有&lt;a href="/archives/2007/02/16/284/"&gt;兩年半前相似的體悟&lt;/a&gt;，到外邊小晃了一圈，我似乎走回正確的路了。即使不知道方向，不知道終點何在，我明白自己正踏在想走的路。&lt;/p&gt;
&lt;p&gt;題外話，看到現在學習程式語言和機器學習的進展，不禁懷疑過去為何期望這麼久，卻遲遲沒有行動。才想起以前還有許多想做的事，像是增加電腦科學的廣度認知、寫 3D 遊戲、參加各式比賽、做研究寫論文等。許多事若沒有親身嘗試，只會念念不忘，難以打消念頭。經過前幾年的嘗試，才能像現在般沉澱心靈，專心在最有興趣的幾個點上。&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/fcamel/~4/AAmWs2l3ivM" height="1" width="1"/&gt;</content>
		<link rel="replies" type="text/html" href="http://fcamel.twbbs.org/archives/2009/05/11/842/#comments" thr:count="7" />
		<link rel="replies" type="application/atom+xml" href="http://fcamel.twbbs.org/archives/2009/05/11/842/feed/atom/" thr:count="7" />
		<thr:total>7</thr:total>
	<feedburner:origLink>http://fcamel.twbbs.org/archives/2009/05/11/842/</feedburner:origLink></entry>
		<entry>
		<author>
			<name>fcamel</name>
					</author>
		<title type="html"><![CDATA[空之軌跡]]></title>
		<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/fcamel/~3/t_Vt3K6feII/" />
		<id>http://fcamel.twbbs.org/?p=834</id>
		<updated>2009-05-02T06:23:38Z</updated>
		<published>2009-05-02T05:32:16Z</published>
		<category scheme="http://fcamel.twbbs.org" term="ACG" /><category scheme="http://fcamel.twbbs.org" term="All" /><category scheme="http://fcamel.twbbs.org" term="Game" />		<summary type="html"><![CDATA[如果不全破的話，是不是故事就可以持續下去永不結束？玩完《空之軌跡》系列後，這樣的想法浮現在我腦裡。這是去年十一月、十二月的事了，愈是喜歡一個作品，愈難下筆。


《空之軌跡》是 Falcom 公司出的系列作，分別為 《空之軌跡 FC》、《空之軌跡 SC》、《空之軌跡 The 3rd》。FC 和 SC 的劇情直接相連，當初全破 FC 後發現劇情急轉直下，嘴裡雖然碎碎唸罵著 Falcom 沒天良，還是立即買了 SC 回來玩。沒想到 SC 故事又比 FC 更龐大，算算兩部玩下來，花了我一百小時，這輩子從來沒這麼密集地玩同一作品。

全破 SC 後，全身充斥著滿滿的感動，又一次地為自己無法以言語表達感動而感到無力，很想分享澎湃的情感，卻擠不出一個字來。就像做了一個很長很美好的夢一般，夢醒後又回到現實世界，接著是深沉的失落感。為了紓解這種失落感，只好上 ptt 看著大家的心得，放著《空之軌跡》的背景音樂，透過不同人從鍵盤下傳達過來的感動，暫時沉浸在《空之軌跡》的世界，直到心情恢復平靜。
只是，一聽到劇中音樂時，又會陷入短暫地感傷，懷念著約書亞、艾絲蒂爾等人。劇中主題曲的歌詞寫得很好，配合歌詞聽音樂，很自然地融入當時的情景。想說的話太多卻無法述諸文字，只好藉由音樂穿插帶出我片段的感受。
《銀の意志、金の翼》，這首當作早晨起床音樂挺不錯的，情緒會很 high。FC 尾聲出現這首時，立即提昇戰鬥氣氛至最高點。

《星の在り処》 (星之所在)，FC 片尾曲。配合歌詞聽著，想到艾絲蒂爾當時的感受，眼眶情不自盡地溼了。 ( 含歌詞的版本見這裡，畫質音質較差。 )
	
《I Swear》，SC 片尾曲。SC 的結束，暗示著艾絲蒂爾等人的旅程暫時到一段落，今後她們也會繼續活躍著。整首歌充斥著希望，但想到故事到此完結，失落感也隨之來襲。

於是我忍不住買了 The 3rd，只為多看他們幾眼。劇情方面，The 3rd 穿插許多支線劇情，帶出各角色之後的發展，大大滿足玩完 SC 的失落感。不過主線劇情相較 FC + SC，遜色不少。在看完 The 3rd 的片尾，畫面上映出「Thank you for playing」的字樣時，我更想和制作小組道謝，謝謝他們做出這樣感人的作品。也不禁想著，那一天我才能參與這樣的大作，將我的感動傳出去呢？
最後，以 The 3rd 的片尾曲，《空を见上げて》，做為結尾吧，可惜只有音樂無畫面：


再見了，《空之軌跡》，謝謝你帶給我這段美好的日子。期許著有那麼一天，我也能將你傳達給我的感動，透過我的雙手，制作出足以感動他人的作品。
]]></summary>
		<content type="html" xml:base="http://fcamel.twbbs.org/archives/2009/05/02/834/">&lt;p&gt;如果不全破的話，是不是故事就可以持續下去永不結束？玩完《空之軌跡》系列後，這樣的想法浮現在我腦裡。這是去年十一月、十二月的事了，愈是喜歡一個作品，愈難下筆。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/12203797@N00/3493169060/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.flickr.com');" title="FC by fcamel, on Flickr"&gt;&lt;img src="http://farm4.static.flickr.com/3655/3493169060_32d21b820c.jpg" width="500" height="375" alt="FC" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span id="more-834"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;《空之軌跡》是 &lt;a href="http://www.falcom.co.jp/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.falcom.co.jp');"&gt;Falcom&lt;/a&gt; 公司出的系列作，分別為 《空之軌跡 FC》、《空之軌跡 SC》、《空之軌跡 The 3rd》。FC 和 SC 的劇情直接相連，當初全破 FC 後發現劇情急轉直下，嘴裡雖然碎碎唸罵著 &lt;a href="http://www.falcom.co.jp/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.falcom.co.jp');"&gt;Falcom&lt;/a&gt; 沒天良，還是立即買了 SC 回來玩。沒想到 SC 故事又比 FC 更龐大，算算兩部玩下來，花了我一百小時，這輩子從來沒這麼密集地玩同一作品。&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/12203797@N00/3492351015/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.flickr.com');" title="SC by fcamel, on Flickr"&gt;&lt;img src="http://farm4.static.flickr.com/3620/3492351015_243f0182f7.jpg" width="500" height="375" alt="SC" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;全破 SC 後，全身充斥著滿滿的感動，又一次地為自己無法以言語表達感動而感到無力，很想分享澎湃的情感，卻擠不出一個字來。就像做了一個很長很美好的夢一般，夢醒後又回到現實世界，接著是深沉的失落感。為了紓解這種失落感，只好上 ptt 看著大家的心得，放著《空之軌跡》的背景音樂，透過不同人從鍵盤下傳達過來的感動，暫時沉浸在《空之軌跡》的世界，直到心情恢復平靜。&lt;/p&gt;
&lt;p&gt;只是，一聽到劇中音樂時，又會陷入短暫地感傷，懷念著約書亞、艾絲蒂爾等人。劇中主題曲的歌詞寫得很好，配合歌詞聽音樂，很自然地融入當時的情景。想說的話太多卻無法述諸文字，只好藉由音樂穿插帶出我片段的感受。&lt;/p&gt;
&lt;p&gt;《銀の意志、金の翼》，這首當作早晨起床音樂挺不錯的，情緒會很 high。FC 尾聲出現這首時，立即提昇戰鬥氣氛至最高點。&lt;br /&gt;
&lt;object width="560" height="340"&gt;&lt;param name="movie" value="http://www.youtube.com/v/15nhOeQQkR4&amp;#038;hl=zh_TW&amp;#038;fs=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/15nhOeQQkR4&amp;#038;hl=zh_TW&amp;#038;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="560" height="340"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/p&gt;
&lt;p&gt;《星の在り処》 (星之所在)，FC 片尾曲。配合歌詞聽著，想到艾絲蒂爾當時的感受，眼眶情不自盡地溼了。 ( 含歌詞的版本見&lt;a href="http://www.youtube.com/watch?v=xIA-BLl6mdw" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.youtube.com');"&gt;這裡&lt;/a&gt;，畫質音質較差。 )&lt;br /&gt;
&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/MMj0rFTybQ8&amp;#038;hl=zh_TW&amp;#038;fs=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/MMj0rFTybQ8&amp;#038;hl=zh_TW&amp;#038;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;	&lt;/p&gt;
&lt;p&gt;《I Swear》，SC 片尾曲。SC 的結束，暗示著艾絲蒂爾等人的旅程暫時到一段落，今後她們也會繼續活躍著。整首歌充斥著希望，但想到故事到此完結，失落感也隨之來襲。&lt;br /&gt;
&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/fndkQ6SpG7M&amp;#038;hl=zh_TW&amp;#038;fs=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/fndkQ6SpG7M&amp;#038;hl=zh_TW&amp;#038;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/p&gt;
&lt;p&gt;於是我忍不住買了 The 3rd，只為多看他們幾眼。劇情方面，The 3rd 穿插許多支線劇情，帶出各角色之後的發展，大大滿足玩完 SC 的失落感。不過主線劇情相較 FC + SC，遜色不少。在看完 The 3rd 的片尾，畫面上映出「Thank you for playing」的字樣時，我更想和制作小組道謝，謝謝他們做出這樣感人的作品。也不禁想著，那一天我才能參與這樣的大作，將我的感動傳出去呢？&lt;/p&gt;
&lt;p&gt;最後，以 The 3rd 的片尾曲，《空を见上げて》，做為結尾吧，可惜只有音樂無畫面：&lt;br /&gt;
&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/X0-PMjN0LTk&amp;#038;hl=zh_TW&amp;#038;fs=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/X0-PMjN0LTk&amp;#038;hl=zh_TW&amp;#038;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/12203797@N00/3493427720/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.flickr.com');" title="sora-no-kiseki-3rd-ed-image by fcamel, on Flickr"&gt;&lt;img src="http://farm4.static.flickr.com/3326/3493427720_01e8f3d519.jpg" width="500" height="494" alt="sora-no-kiseki-3rd-ed-image" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;再見了，《空之軌跡》，謝謝你帶給我這段美好的日子。期許著有那麼一天，我也能將你傳達給我的感動，透過我的雙手，制作出足以感動他人的作品。&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/fcamel/~4/t_Vt3K6feII" height="1" width="1"/&gt;</content>
		<link rel="replies" type="text/html" href="http://fcamel.twbbs.org/archives/2009/05/02/834/#comments" thr:count="3" />
		<link rel="replies" type="application/atom+xml" href="http://fcamel.twbbs.org/archives/2009/05/02/834/feed/atom/" thr:count="3" />
		<thr:total>3</thr:total>
	<feedburner:origLink>http://fcamel.twbbs.org/archives/2009/05/02/834/</feedburner:origLink></entry>
		<entry>
		<author>
			<name>fcamel</name>
					</author>
		<title type="html"><![CDATA[Python 排序的技巧]]></title>
		<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/fcamel/~3/9dHp-s4iCII/" />
		<id>http://fcamel.twbbs.org/?p=825</id>
		<updated>2009-05-01T17:23:08Z</updated>
		<published>2009-05-01T12:18:26Z</published>
		<category scheme="http://fcamel.twbbs.org" term="All" /><category scheme="http://fcamel.twbbs.org" term="Programming" /><category scheme="http://fcamel.twbbs.org" term="Python" />		<summary type="html"><![CDATA[用 Python 排序時相較於 C、Java 多了效率議題。由於 Python 內建函數 (built-in function) 是用 C 實作的，排序時盡可能用內建函數效率會快上不少，詳細的數據比較可以參見 Python Cookbook，這篇做個簡單的整理。

背景知識
本篇需要 list comprehension 和 map 的基本觀念，若不清楚的話可以先了解它們再往下看。
排序 list 和 dict
先來看程式碼：

1
2
3
4
5
6
7
8
9
10
11
12
13
d = &#123; 'a': 2, 'b': 1 &#125;
# return keys ordered by key
print sorted&#40;d&#41;                     [...]]]></summary>
		<content type="html" xml:base="http://fcamel.twbbs.org/archives/2009/05/01/825/">&lt;p&gt;用 Python 排序時相較於 C、Java 多了效率議題。由於 Python 內建函數 (built-in function) 是用 C 實作的，排序時盡可能用內建函數效率會快上不少，詳細的數據比較可以參見 &lt;a href="http://oreilly.com/catalog/9780596001674/" onclick="javascript:pageTracker._trackPageview('/outbound/article/oreilly.com');"&gt;Python Cookbook&lt;/a&gt;，這篇做個簡單的整理。&lt;/p&gt;
&lt;p&gt;&lt;span id="more-825"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;背景知識&lt;/h4&gt;
&lt;p&gt;本篇需要 &lt;a href="http://docs.python.org/howto/functional.html#generator-expressions-and-list-comprehensions" onclick="javascript:pageTracker._trackPageview('/outbound/article/docs.python.org');"&gt;list comprehension&lt;/a&gt; 和 &lt;a href="http://docs.python.org/howto/functional.html#built-in-functions" onclick="javascript:pageTracker._trackPageview('/outbound/article/docs.python.org');"&gt;map&lt;/a&gt; 的基本觀念，若不清楚的話可以先了解它們再往下看。&lt;/p&gt;
&lt;h4&gt;排序 list 和 dict&lt;/h4&gt;
&lt;p&gt;先來看程式碼：&lt;/p&gt;

&lt;div class="wp_syntax"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="line_numbers"&gt;&lt;pre&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre class="python" style="font-family:monospace;"&gt;d = &lt;span style="color: black;"&gt;&amp;#123;&lt;/span&gt; &lt;span style="color: #483d8b;"&gt;'a'&lt;/span&gt;: &lt;span style="color: #ff4500;"&gt;2&lt;/span&gt;, &lt;span style="color: #483d8b;"&gt;'b'&lt;/span&gt;: &lt;span style="color: #ff4500;"&gt;1&lt;/span&gt; &lt;span style="color: black;"&gt;&amp;#125;&lt;/span&gt;
&lt;span style="color: #808080; font-style: italic;"&gt;# return keys ordered by key&lt;/span&gt;
&lt;span style="color: #ff7700;font-weight:bold;"&gt;print&lt;/span&gt; &lt;span style="color: #008000;"&gt;sorted&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;d&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;                                      &lt;span style="color: #808080; font-style: italic;"&gt;# ['a', 'b']&lt;/span&gt;
&lt;span style="color: #808080; font-style: italic;"&gt;# return values ordered by key&lt;/span&gt;
&lt;span style="color: #ff7700;font-weight:bold;"&gt;print&lt;/span&gt; &lt;span style="color: #008000;"&gt;map&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;d.&lt;span style="color: black;"&gt;get&lt;/span&gt;, &lt;span style="color: #008000;"&gt;sorted&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;d&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;                          &lt;span style="color: #808080; font-style: italic;"&gt;# [2, 1]&lt;/span&gt;
&lt;span style="color: #808080; font-style: italic;"&gt;# return keys ordered by value&lt;/span&gt;
&lt;span style="color: #ff7700;font-weight:bold;"&gt;print&lt;/span&gt; &lt;span style="color: #008000;"&gt;sorted&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;d, key=d.&lt;span style="color: black;"&gt;get&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;                           &lt;span style="color: #808080; font-style: italic;"&gt;# ['b', 'a']&lt;/span&gt;
&lt;span style="color: #808080; font-style: italic;"&gt;# return values ordered by value&lt;/span&gt;
&lt;span style="color: #ff7700;font-weight:bold;"&gt;print&lt;/span&gt; &lt;span style="color: #008000;"&gt;sorted&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;d.&lt;span style="color: black;"&gt;values&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;                             &lt;span style="color: #808080; font-style: italic;"&gt;# [1, 2]&lt;/span&gt;
&lt;span style="color: #808080; font-style: italic;"&gt;# return (key, value) pairs order by key&lt;/span&gt;
&lt;span style="color: #ff7700;font-weight:bold;"&gt;print&lt;/span&gt; &lt;span style="color: #008000;"&gt;sorted&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;d.&lt;span style="color: black;"&gt;items&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;                              &lt;span style="color: #808080; font-style: italic;"&gt;# [('a', 2), ('b', 1)]&lt;/span&gt;
&lt;span style="color: #808080; font-style: italic;"&gt;# return (key, value) pairs order by value&lt;/span&gt;
&lt;span style="color: #ff7700;font-weight:bold;"&gt;print&lt;/span&gt; &lt;span style="color: #008000;"&gt;sorted&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;d.&lt;span style="color: black;"&gt;items&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;, key=&lt;span style="color: #dc143c;"&gt;operator&lt;/span&gt;.&lt;span style="color: black;"&gt;itemgetter&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;&lt;span style="color: #ff4500;"&gt;1&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;  &lt;span style="color: #808080; font-style: italic;"&gt;# [('b', 1), ('a', 2)]&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;上面的程式列出所有排序 dict ( 在一些語言裡稱為 hash table) 的方法，這裡用到幾個技巧，分別說明一下。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;return values ordered by key：&lt;/strong&gt;Python 的 method call 其實和 function call 差不多，obj.func(x) 相當於傳回 func(obj, x)，所以 d.get 一樣可以傳進 map 裡，結果是會取回 d 的 value。關於 function 和 method 的說明，可以參考&lt;a href="http://codeplayer.blogspot.com/2006/12/python-method-function-descriptor.html" onclick="javascript:pageTracker._trackPageview('/outbound/article/codeplayer.blogspot.com');"&gt;《理解 python 的 method 和 function 兼谈 descriptor 》&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;return keys ordered by value：&lt;/strong&gt;同上的技巧，注意參數 key 有做最佳化處理，使得 d 的每個元素只會呼叫一次 d.get ( 而不是比較次數 O(N*logN) 次 )，不用擔心取 key 的函式會被重覆執行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;return (key, value) pairs order by value：&lt;/strong&gt;這部份的 code 和排序 list 一樣，關鍵在於用了 operator.itemgetter，它的文件說明摘錄如下：&lt;br /&gt;
&lt;blockquote&gt;
Return a callable object that fetches the given item(s) from its operand.&lt;br /&gt;
After, f=itemgetter(2), the call f(r) returns r[2].
&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;以程式碼來表示的話，類似於這麼寫：&lt;/p&gt;

&lt;div class="wp_syntax"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="line_numbers"&gt;&lt;pre&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre class="python" style="font-family:monospace;"&gt;&lt;span style="color: #ff7700;font-weight:bold;"&gt;def&lt;/span&gt; itemgetter&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;n&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;:
    &lt;span style="color: #ff7700;font-weight:bold;"&gt;return&lt;/span&gt; &lt;span style="color: #ff7700;font-weight:bold;"&gt;lambda&lt;/span&gt; r: r&lt;span style="color: black;"&gt;&amp;#91;&lt;/span&gt;n&lt;span style="color: black;"&gt;&amp;#93;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;只要資料轉成 list of lists 的形式，就可以用 operator.itemgetter 處理。轉成 list of lists 的時間和資料數量成正比，相對於套入用 python 寫的 comparator 來說，快上不少。module operator 裡有許多好用的函式，在套用 map、max、sort 等內建函式時，可以配合使用，一來不用自己重寫小函式，二來內建函式用 C 實作，速度快上許多。&lt;/p&gt;
&lt;h4&gt;Decorate-Sort-Undecorate (DSU)&lt;/h4&gt;
&lt;p&gt;若要比較的方式比較複雜，像是希望有 &lt;a href="http://en.wikipedia.org/wiki/Sorting_algorithm#Stability" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;stable sort&lt;/a&gt;，或是比較的 key 不只一個，可以套用 DSU 的解法。這裡以 stable sort 為例 (程式引用自 &lt;a href="http://oreilly.com/catalog/9780596001674/" onclick="javascript:pageTracker._trackPageview('/outbound/article/oreilly.com');"&gt;Python Cookbook&lt;/a&gt;)：&lt;/p&gt;

&lt;div class="wp_syntax"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="line_numbers"&gt;&lt;pre&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre class="python" style="font-family:monospace;"&gt;&lt;span style="color: #ff7700;font-weight:bold;"&gt;def&lt;/span&gt; stable_sorted_copy&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;alist, _indices=&lt;span style="color: #008000;"&gt;xrange&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;&lt;span style="color: #dc143c;"&gt;sys&lt;/span&gt;.&lt;span style="color: black;"&gt;maxint&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;:
    decorated = &lt;span style="color: #008000;"&gt;zip&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;alist, _indices&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;
    decorated.&lt;span style="color: black;"&gt;sort&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;
    &lt;span style="color: #ff7700;font-weight:bold;"&gt;return&lt;/span&gt; &lt;span style="color: black;"&gt;&amp;#91;&lt;/span&gt; item &lt;span style="color: #ff7700;font-weight:bold;"&gt;for&lt;/span&gt; item, index &lt;span style="color: #ff7700;font-weight:bold;"&gt;in&lt;/span&gt; decorated &lt;span style="color: black;"&gt;&amp;#93;&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;DSU 的關鍵在於先把每筆資料轉成 [key1, key2, ..., original item] 的形式，如此一來內建的 sort 就會依 key1, key2, &amp;#8230; 的順序排序，排好後再取回 original item。同樣的，排序 object 依欄位值或 method 傳回值，都可以套用 DSU。而 &lt;a href="http://en.wikipedia.org/wiki/Sorting_algorithm#Stability" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;stable sort&lt;/a&gt; 的情況比較不同，是將資料轉成 [original item, original position ]，排序後再取回 original item。&lt;/p&gt;
&lt;p&gt;附帶一提，xrange 是 &lt;a href="http://docs.python.org/howto/functional.html#generators" onclick="javascript:pageTracker._trackPageview('/outbound/article/docs.python.org');"&gt;generator&lt;/a&gt;，使用 generator 好處可以參見 &lt;a href="http://www.python.org/dev/peps/pep-0289/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.python.org');"&gt;PEP 289: Generator Expressions&lt;/a&gt;。&lt;/p&gt;
&lt;h4&gt;參考資料&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://oreilly.com/catalog/9780596001674/" onclick="javascript:pageTracker._trackPageview('/outbound/article/oreilly.com');"&gt;Python Cookbook&lt;/a&gt; 第二章 Searching and Sorting：有詳細的例子一步步解釋各種寫法，並分析優劣。&lt;/li&gt;
&lt;li&gt;&lt;a href="http://wiki.python.org/moin/HowTo/Sorting#Sortingbykeys" onclick="javascript:pageTracker._trackPageview('/outbound/article/wiki.python.org');"&gt;Sorting Mini-HOW TO - Sorting by keys&lt;/a&gt;：這篇比 Cookbook 的介紹精簡許多。&lt;/li&gt;
&lt;li&gt;&lt;a href="http://docs.python.org/howto/functional.html" onclick="javascript:pageTracker._trackPageview('/outbound/article/docs.python.org');"&gt;Functional Programming HOWTO&lt;/a&gt;：這篇清楚地說明許多重要的基本觀念。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;備註&lt;/h4&gt;
&lt;p&gt;發表這篇後，才發現去年的今天我在寫 Ruby。物換星移，忽然有淡淡的懷舊感傷。&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/fcamel/~4/9dHp-s4iCII" height="1" width="1"/&gt;</content>
		<link rel="replies" type="text/html" href="http://fcamel.twbbs.org/archives/2009/05/01/825/#comments" thr:count="0" />
		<link rel="replies" type="application/atom+xml" href="http://fcamel.twbbs.org/archives/2009/05/01/825/feed/atom/" thr:count="0" />
		<thr:total>0</thr:total>
	<feedburner:origLink>http://fcamel.twbbs.org/archives/2009/05/01/825/</feedburner:origLink></entry>
		<entry>
		<author>
			<name>fcamel</name>
					</author>
		<title type="html"><![CDATA[關西行：廁所大賞]]></title>
		<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/fcamel/~3/7WAXIkuKTVY/" />
		<id>http://fcamel.twbbs.org/?p=820</id>
		<updated>2009-04-28T13:49:49Z</updated>
		<published>2009-04-28T13:44:49Z</published>
		<category scheme="http://fcamel.twbbs.org" term="All" /><category scheme="http://fcamel.twbbs.org" term="Travel" />		<summary type="html"><![CDATA[從日本玩回來都快過一年了遊記還沒完成，還是直接寫最有興趣的這篇，剩下的就隨意吧。

說到旅遊方面的事，我可能沒什麼特別自信，但去一個旅遊景點先找廁所這件事，我可是很在行呢！這回去關西特別搜集了多所精美廁所外觀，在這裡和大家分享一下。為什麼沒拍廁所內部呢？一來是內部沒差太多，都是現代化設施；二來是我不想在一個人旅遊時以妨礙風化的罪行被逮補。這次的特集裡，最可惜的是沒拍到清水舞台下面的廁所，那間可說是此行中的冠亞軍之選，但人潮太多，我在那等了十多分鐘都抓不到拍照的時機。
廢話不多說，首先是金閣寺的廁所：

所照片所示，以金閣寺的名聲來說，未免太普通了點，失敗失敗。

上面這張是龍安寺石庭旁的廁所，也是相當普通。再來這張就有點意思了：

仁和寺的廁所看來比較有點格調，為了怕我自己認不出來是廁所，故意選照得到水管的角度。以在關西的第一天來說，就仁和寺的廁所有在水準之內。希望這些水準低下的廁所外觀沒有掃了大家逛大阪的興致，比較正常的風景照和介紹請參見《關西行：抵達京都、前往洛西》。

再來這張是風俗博物館內部的廁所，由於該館很小，外觀．．．就是一扇門，只好改拍內部意思意思，可以看到中間還放了幾個小裝飾。喜歡源氏物語的人可以去看看，詳見之前寫的《關西行：京都洛中》。
再貼這類照片下去，相信大家很想關網頁了，只好提前先拿出最有冠軍相的平等院廁所！

是不是看起來相當雅緻呢？而我個人相當中意的伏見稻荷大社，相當地可惜，山中的還算上得了檯面，可是在人潮多的神社旁邊，卻是相當現代化的外觀，真是太不專業了。

( 神社外的廁所 )

( 山中的廁所，拍這張花了我一點時間，雖然在山中，人潮也不算少 )
關於上兩間廁所之外的正常介紹，請見舊文《關西行：京都洛南》。
下面這兩張是二条城的廁所，馬馬虎虎，將就將就啦，護城河和城牆這麼美，為什麼廁所卻沒那種霸氣呢？


而下面這張現代化的廁所，竟然位在清水寺，裝個屋瓦並不會改變現代化外觀的事實，真是太令人失望了。所幸在清水寺下面的廁所相當雅緻，為清水寺扳回一些面子。只可惜人潮過多我沒能照成，可說是本次旅遊的一大憾事。

這部份的相關介紹，請見《關西行：京都洛中二条城及洛東清水寺》
最後這張是大阪城公園的廁所，外觀看來頗為氣派，但如同水柱邊泛黃的痕跡所示，廁所內部實在太髒啦！

就這樣，本次的廁所大賞到一段落，感謝各位帶著被玷污的眼睛耐心看完。不知大家心目中關西最漂亮的廁所是那間呢？
最後的最後，我不得不說，TD-Post真是好用啊！
]]></summary>
		<content type="html" xml:base="http://fcamel.twbbs.org/archives/2009/04/28/820/">&lt;p&gt;從日本玩回來都快過一年了遊記還沒完成，還是直接寫最有興趣的這篇，剩下的就隨意吧。&lt;/p&gt;
&lt;p&gt;&lt;span id="more-820"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;說到旅遊方面的事，我可能沒什麼特別自信，但去一個旅遊景點&lt;strong&gt;先找廁所這件事，我可是很在行呢！&lt;/strong&gt;這回去關西特別搜集了多所精美廁所外觀，在這裡和大家分享一下。為什麼沒拍廁所內部呢？一來是內部沒差太多，都是現代化設施；二來是我不想在一個人旅遊時以妨礙風化的罪行被逮補。這次的特集裡，最可惜的是沒拍到&lt;a href="http://zh.wikipedia.org/wiki/清水寺" onclick="javascript:pageTracker._trackPageview('/outbound/article/zh.wikipedia.org');"&gt;清水舞台&lt;/a&gt;下面的廁所，那間可說是此行中的冠亞軍之選，但人潮太多，我在那等了十多分鐘都抓不到拍照的時機。&lt;/p&gt;
&lt;p&gt;廢話不多說，首先是&lt;a href="http://zh.wikipedia.org/wiki/金閣寺" onclick="javascript:pageTracker._trackPageview('/outbound/article/zh.wikipedia.org');"&gt;金閣寺&lt;/a&gt;的廁所：&lt;/p&gt;
&lt;p&gt;&lt;img src="http://p4.p.pixnet.net/albums/userpics/4/9/253349/normal_1212127929.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;所照片所示，以&lt;a href="http://zh.wikipedia.org/wiki/金閣寺" onclick="javascript:pageTracker._trackPageview('/outbound/article/zh.wikipedia.org');"&gt;金閣寺&lt;/a&gt;的名聲來說，未免太普通了點，失敗失敗。&lt;/p&gt;
&lt;p&gt;&lt;img src="http://p4.p.pixnet.net/albums/userpics/4/9/253349/normal_1212127978.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;上面這張是&lt;a href="http://zh.wikipedia.org/wiki/龍安寺" onclick="javascript:pageTracker._trackPageview('/outbound/article/zh.wikipedia.org');"&gt;龍安寺&lt;/a&gt;石庭旁的廁所，也是相當普通。再來這張就有點意思了：&lt;/p&gt;
&lt;p&gt;&lt;img src="http://p4.p.pixnet.net/albums/userpics/4/9/253349/normal_1212128029.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;仁和寺的廁所看來比較有點格調，為了怕我自己認不出來是廁所，故意選照得到水管的角度。以在關西的第一天來說，就仁和寺的廁所有在水準之內。希望這些水準低下的廁所外觀沒有掃了大家逛大阪的興致，比較正常的風景照和介紹請參見&lt;a href="/archives/2008/06/21/491/"&gt;《關西行：抵達京都、前往洛西》&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img src="http://p4.p.pixnet.net/albums/userpics/4/9/253349/normal_1212128158.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;再來這張是風俗博物館內部的廁所，由於該館很小，外觀．．．就是一扇門，只好改拍內部意思意思，可以看到中間還放了幾個小裝飾。喜歡源氏物語的人可以去看看，詳見之前寫的&lt;a href="/archives/2009/02/01/591/"&gt;《關西行：京都洛中》&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;再貼這類照片下去，相信大家很想關網頁了，只好提前先拿出&lt;strong&gt;最有冠軍相的&lt;a href="http://zh.wikipedia.org/wiki/平等院" onclick="javascript:pageTracker._trackPageview('/outbound/article/zh.wikipedia.org');"&gt;平等院&lt;/a&gt;廁所！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="http://p4.p.pixnet.net/albums/userpics/4/9/253349/normal_1212128295.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;是不是看起來相當雅緻呢？而我個人相當中意的&lt;a href="http://zh.wikipedia.org/wiki/伏見稻荷大社" onclick="javascript:pageTracker._trackPageview('/outbound/article/zh.wikipedia.org');"&gt;伏見稻荷大社&lt;/a&gt;，相當地可惜，山中的還算上得了檯面，可是在人潮多的神社旁邊，卻是相當現代化的外觀，真是太不專業了。&lt;/p&gt;
&lt;p&gt;&lt;img src="http://p4.p.pixnet.net/albums/userpics/4/9/253349/normal_1212128276.jpg" /&gt;&lt;br /&gt;
( 神社外的廁所 )&lt;/p&gt;
&lt;p&gt;&lt;img src="http://p4.p.pixnet.net/albums/userpics/4/9/253349/normal_1212128245.jpg" /&gt;&lt;br /&gt;
( 山中的廁所，拍這張花了我一點時間，雖然在山中，人潮也不算少 )&lt;/p&gt;
&lt;p&gt;關於上兩間廁所之外的正常介紹，請見舊文&lt;a href="/archives/2009/02/02/611/"&gt;《關西行：京都洛南》&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;下面這兩張是二条城的廁所，馬馬虎虎，將就將就啦，護城河和城牆這麼美，為什麼廁所卻沒那種霸氣呢？&lt;/p&gt;
&lt;p&gt;&lt;img src="http://p4.p.pixnet.net/albums/userpics/4/9/253349/normal_1212128364.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="http://p4.p.pixnet.net/albums/userpics/4/9/253349/normal_1212128414.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;而下面這張&lt;strong&gt;現代化的廁所，竟然位在&lt;a href="http://zh.wikipedia.org/wiki/清水寺" onclick="javascript:pageTracker._trackPageview('/outbound/article/zh.wikipedia.org');"&gt;清水寺&lt;/a&gt;，裝個屋瓦並不會改變現代化外觀的事實，&lt;/strong&gt;真是太令人失望了。所幸在&lt;a href="http://zh.wikipedia.org/wiki/清水寺" onclick="javascript:pageTracker._trackPageview('/outbound/article/zh.wikipedia.org');"&gt;清水寺&lt;/a&gt;下面的廁所相當雅緻，為&lt;a href="http://zh.wikipedia.org/wiki/清水寺" onclick="javascript:pageTracker._trackPageview('/outbound/article/zh.wikipedia.org');"&gt;清水寺&lt;/a&gt;扳回一些面子。只可惜人潮過多我沒能照成，可說是本次旅遊的一大憾事。&lt;/p&gt;
&lt;p&gt;&lt;img src="http://p4.p.pixnet.net/albums/userpics/4/9/253349/normal_1212128483.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;這部份的相關介紹，請見&lt;a href="/archives/2009/02/04/634/"&gt;《關西行：京都洛中二条城及洛東清水寺》&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;最後這張是&lt;a href="http://ja.wikipedia.org/wiki/大阪城公園" onclick="javascript:pageTracker._trackPageview('/outbound/article/ja.wikipedia.org');"&gt;大阪城公園&lt;/a&gt;的廁所，外觀看來頗為氣派，但如同水柱邊泛黃的痕跡所示，廁所內部實在太髒啦！&lt;/p&gt;
&lt;p&gt;&lt;img src="http://p4.p.pixnet.net/albums/userpics/4/9/253349/normal_1212128931.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;就這樣，本次的廁所大賞到一段落，感謝各位帶著被玷污的眼睛耐心看完。不知大家心目中關西最漂亮的廁所是那間呢？&lt;/p&gt;
&lt;p&gt;最後的最後，我不得不說，&lt;a href="/archives/2009/04/25/809/"&gt;TD-Post&lt;/a&gt;真是好用啊！&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/fcamel/~4/7WAXIkuKTVY" height="1" width="1"/&gt;</content>
		<link rel="replies" type="text/html" href="http://fcamel.twbbs.org/archives/2009/04/28/820/#comments" thr:count="2" />
		<link rel="replies" type="application/atom+xml" href="http://fcamel.twbbs.org/archives/2009/04/28/820/feed/atom/" thr:count="2" />
		<thr:total>2</thr:total>
	<feedburner:origLink>http://fcamel.twbbs.org/archives/2009/04/28/820/</feedburner:origLink></entry>
		<entry>
		<author>
			<name>fcamel</name>
					</author>
		<title type="html"><![CDATA[寫 Blog 好幫手 TD-Post！]]></title>
		<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/fcamel/~3/71_XscnBTkw/" />
		<id>http://fcamel.twbbs.org/?p=809</id>
		<updated>2009-04-25T15:32:56Z</updated>
		<published>2009-04-25T15:05:40Z</published>
		<category scheme="http://fcamel.twbbs.org" term="All" /><category scheme="http://fcamel.twbbs.org" term="Blog" /><category scheme="http://fcamel.twbbs.org" term="Programming" /><category scheme="http://fcamel.twbbs.org" term="Python" /><category scheme="http://fcamel.twbbs.org" term="Testing" /><category scheme="http://fcamel.twbbs.org" term="Web" /><category scheme="http://fcamel.twbbs.org" term="Wiki" />		<summary type="html"><![CDATA[為了方便寫 Blog，寫了個小程式讀 wiki code 產生 WordPress 吃的格式，順便藉機練習 TDD，沒想到寫了十小時才完成。剛好最近在看虎x龍的動畫，就將它命名為TD-Post (Tiger x Dragon Post) 吧。

緣起
換過多家 WordPress 用的編輯器後，我還是找不到滿意的工具。為縮短寫 Blog 的時間，我決定自己做一個。
寫 Blog 最惱人的事有幾點：

得輸入麻煩的 HTML，特別是要匹配結束標籤特別麻煩。而且 HTML code 不易閱讀，改文章時很不方便。
加超鏈結很麻煩，常用的幾個超鏈結，得重附貼多次，像 TDD 我就重貼了許多次。
無法用自己慣用的編輯器。若能用 Vim 寫 Blog，速度必能大增啊！

功能簡介
對我來說，寫 wiki code 相當方便，所以輸入格式決定用 wiki code。但 wiki 格式有許多種，我比較常用的有 PmWiki、DokuWiki、TWiki，其中以 PmWiki 語法最簡單，但原始碼沒後兩者好讀。最後聽從 York 的建議，採用 Trac 的 wiki 格式。選它的主要原因除好讀好寫外，我特別喜歡它 Preformatted Text 的格式，很適合用來貼程式碼。
接著，針對第二點問題，加上自動補超鏈結的功能，只要寫過一次超鏈結，TD-Post 就會自動記下來，自動補上對應的位置。像在這段裡，因為我在第一段已寫過 TD-Post 的位置， TD-Post 的超鏈結都會自動補上。初步估計，以後寫一篇 Blog 至少可以省十分鐘，所以差不多用個六十次．．．這幾天的辛勞就．就回本啦。
程式下載
本程式採 BSD [...]]]></summary>
		<content type="html" xml:base="http://fcamel.twbbs.org/archives/2009/04/25/809/">&lt;p&gt;為了方便寫 Blog，寫了個小程式讀 wiki code 產生 &lt;a href="http://wordpress.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/wordpress.org');"&gt;WordPress&lt;/a&gt; 吃的格式，順便藉機練習 &lt;a href="http://en.wikipedia.org/wiki/Test-driven_development" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;TDD&lt;/a&gt;，沒想到寫了十小時才完成。剛好最近在看&lt;a href="http://zh.wikipedia.org/wiki/虎與龍_(小說)" onclick="javascript:pageTracker._trackPageview('/outbound/article/zh.wikipedia.org');"&gt;虎x龍&lt;/a&gt;的動畫，就將它命名為&lt;a href="http://code.google.com/p/td-post/" onclick="javascript:pageTracker._trackPageview('/outbound/article/code.google.com');"&gt;TD-Post&lt;/a&gt; (Tiger x Dragon Post) 吧。&lt;/p&gt;
&lt;p&gt;&lt;span id="more-809"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;緣起&lt;/h4&gt;
&lt;p&gt;換過多家 &lt;a href="http://wordpress.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/wordpress.org');"&gt;WordPress&lt;/a&gt; 用的編輯器後，我還是找不到滿意的工具。為縮短寫 Blog 的時間，我決定自己做一個。&lt;/p&gt;
&lt;p&gt;寫 Blog 最惱人的事有幾點：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;得輸入麻煩的 HTML，特別是要匹配結束標籤特別麻煩。而且 HTML code 不易閱讀，改文章時很不方便。&lt;/li&gt;
&lt;li&gt;加超鏈結很麻煩，常用的幾個超鏈結，得重附貼多次，像 &lt;a href="http://en.wikipedia.org/wiki/Test-driven_development" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;TDD&lt;/a&gt; 我就重貼了許多次。&lt;/li&gt;
&lt;li&gt;無法用自己慣用的編輯器。若能用 &lt;a href="http://www.vim.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.vim.org');"&gt;Vim&lt;/a&gt; 寫 Blog，速度必能大增啊！&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;功能簡介&lt;/h4&gt;
&lt;p&gt;對我來說，寫 wiki code 相當方便，所以輸入格式決定用 wiki code。但 wiki 格式有許多種，我比較常用的有 &lt;a href="http://www.pmwiki.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.pmwiki.org');"&gt;PmWiki&lt;/a&gt;、&lt;a href="http://www.dokuwiki.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.dokuwiki.org');"&gt;DokuWiki&lt;/a&gt;、&lt;a href="http://twiki.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/twiki.org');"&gt;TWiki&lt;/a&gt;，其中以 &lt;a href="http://www.pmwiki.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.pmwiki.org');"&gt;PmWiki&lt;/a&gt; 語法最簡單，但原始碼沒後兩者好讀。最後聽從 York 的建議，採用 &lt;a href="http://trac.edgewall.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/trac.edgewall.org');"&gt;Trac&lt;/a&gt; 的 &lt;a href="http://trac.edgewall.org/wiki/WikiFormatting" onclick="javascript:pageTracker._trackPageview('/outbound/article/trac.edgewall.org');"&gt;wiki 格式&lt;/a&gt;。選它的主要原因除好讀好寫外，我特別喜歡它 Preformatted Text 的格式，很適合用來貼程式碼。&lt;/p&gt;
&lt;p&gt;接著，針對第二點問題，加上自動補超鏈結的功能，只要寫過一次超鏈結，&lt;a href="http://code.google.com/p/td-post/" onclick="javascript:pageTracker._trackPageview('/outbound/article/code.google.com');"&gt;TD-Post&lt;/a&gt; 就會自動記下來，自動補上對應的位置。像在這段裡，因為我在第一段已寫過 &lt;a href="http://code.google.com/p/td-post/" onclick="javascript:pageTracker._trackPageview('/outbound/article/code.google.com');"&gt;TD-Post&lt;/a&gt; 的位置， &lt;a href="http://code.google.com/p/td-post/" onclick="javascript:pageTracker._trackPageview('/outbound/article/code.google.com');"&gt;TD-Post&lt;/a&gt; 的超鏈結都會自動補上。初步估計，以後寫一篇 Blog 至少可以省十分鐘，所以差不多用個六十次．．．這幾天的辛勞就．就回本啦。&lt;/p&gt;
&lt;h4&gt;程式下載&lt;/h4&gt;
&lt;p&gt;本程式採 &lt;a href="http://zh.wikipedia.org/w/index.php?title=BSD许可证&amp;#038;variant=zh-tw" onclick="javascript:pageTracker._trackPageview('/outbound/article/zh.wikipedia.org');"&gt;BSD License&lt;/a&gt; ( 簡單說就是隨便使用 )，歡迎大家玩玩。雖然沒什麼註解，但我照 &lt;a href="http://en.wikipedia.org/wiki/Test-driven_development" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;TDD&lt;/a&gt; 流程寫的，測試碼應該很完整。這裡是一些相關資訊：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;下載：&lt;a href="http://code.google.com/p/td-post/downloads/list" onclick="javascript:pageTracker._trackPageview('/outbound/article/code.google.com');"&gt;http://code.google.com/p/td-post/downloads/list&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;程式語言：&lt;a href="http://www.python.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.python.org');"&gt;Python&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;程式碼總行數：682 行&lt;/li&gt;
&lt;li&gt;測式碼：346 行 ( 主要是準備測試的例子 )&lt;/li&gt;
&lt;li&gt;非測試碼：336 行&lt;/li&gt;
&lt;li&gt;實作時間：原本預估兩小時完成，卻花了約十小時。大概是一小時查語法、一到兩小時寫測試。其中花最多的時間在解決 list 和 performatted text 的衝突，這部份從開始實作到結束花了三小時才解決。&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;其它&lt;/h4&gt;
&lt;p&gt;有原始碼有真相，最後附上本文的原始碼：&lt;/p&gt;

&lt;div class="wp_syntax"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="line_numbers"&gt;&lt;pre&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre class="html" style="font-family:monospace;"&gt;為了方便寫 Blog，寫了個小程式讀 wiki code 產生 [http://wordpress.org/ WordPress] 吃的格式，順便藉機練習 [http://en.wikipedia.org/wiki/Test-driven_development TDD]，沒想到寫了十小時才完成。剛好最近在看[http://zh.wikipedia.org/wiki/虎與龍_(小說) 虎x龍]的動畫，就將它命名為[http://code.google.com/p/td-post/ TD-Post] (Tiger x Dragon Post) 吧。
&amp;nbsp;
==== 緣起 ====
換過多家 [WordPress] 用的編輯器後，我還是找不到滿意的工具。為縮短寫 Blog 的時間，我決定自己做一個。
&amp;nbsp;
寫 Blog 最惱人的事有幾點：
 1. 得輸入麻煩的 HTML，特別是要匹配結束標籤特別麻煩。而且 HTML code 不易閱讀，改文章時很不方便。
 1. 加超鏈結很麻煩，常用的幾個超鏈結，得重附貼多次，像 [TDD] 我就重貼了許多次。
 1. 無法用自己慣用的編輯器。若能用 [http://www.vim.org/ Vim] 寫 Blog，速度必能大增啊！
&amp;nbsp;
==== 功能簡介 ====
對我來說，寫 wiki code 相當方便，所以輸入格式決定用 wiki code。但 wiki 格式有許多種，我比較常用的有 [http://www.pmwiki.org/ PmWiki]、[http://www.dokuwiki.org/ DokuWiki]、[http://twiki.org/ TWiki]，其中以 [PmWiki] 語法最簡單，但原始碼沒後兩者好讀。最後聽從 York 的建議，採用 [http://trac.edgewall.org/ Trac] 的 [http://trac.edgewall.org/wiki/WikiFormatting wiki 格式]。選它的主要原因除好讀好寫外，我特別喜歡它 Preformatted Text 的格式，很適合用來貼程式碼。
&amp;nbsp;
接著，針對第二點問題，加上自動補超鏈結的功能，只要寫過一次超鏈結，[TD-Post] 就會自動記下來，自動補上對應的位置。像在這段裡，因為我在第一段已寫過 [TD-Post] 的位置， [TD-Post] 的超鏈結都會自動補上。初步估計，以後寫一篇 Blog 至少可以省十分鐘，所以差不多用個六十次．．．這幾天的辛勞就．就回本啦。
&amp;nbsp;
==== 程式下載 ====
本程式採 [http://zh.wikipedia.org/w/index.php?title=BSD许可证&amp;amp;variant=zh-tw BSD License] ( 簡單說就是隨便使用 )，歡迎大家玩玩。雖然沒什麼註解，但我照 [TDD] 流程寫的，測試碼應該很完整。這裡是一些相關資訊：
 * 下載：http://code.google.com/p/td-post/downloads/list
 * 程式語言：[http://www.python.org/ Python]
 * 程式碼總行數：682 行
 * 測式碼：346 行 ( 主要是準備測試的例子 )
 * 非測試碼：336 行
 * 實作時間：原本預估兩小時完成，卻花了約十小時。大概是一小時查語法、一到兩小時寫測試。其中花最多的時間在解決 list 和 performatted text 的衝突，這部份從開始實作到結束花了三小時才解決。
&amp;nbsp;
==== 其它 ====
&amp;nbsp;
有原始碼有真相，最後附上本文的原始碼：{{{
#!html
_QUINE_
}}}
&amp;nbsp;
身為資工人，這段原始碼當然也是自動貼上的啦，不過我沒用像 [http://en.wikipedia.org/wiki/Quine_(computing) Quine] 那樣的技巧，只是單純地取代關鍵字。&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;身為資工人，這段原始碼當然也是自動貼上的啦，不過我沒用像 &lt;a href="http://en.wikipedia.org/wiki/Quine_(computing)" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;Quine&lt;/a&gt; 那樣的技巧，只是單純地取代關鍵字。&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/fcamel/~4/71_XscnBTkw" height="1" width="1"/&gt;</content>
		<link rel="replies" type="text/html" href="http://fcamel.twbbs.org/archives/2009/04/25/809/#comments" thr:count="7" />
		<link rel="replies" type="application/atom+xml" href="http://fcamel.twbbs.org/archives/2009/04/25/809/feed/atom/" thr:count="7" />
		<thr:total>7</thr:total>
	<feedburner:origLink>http://fcamel.twbbs.org/archives/2009/04/25/809/</feedburner:origLink></entry>
		<entry>
		<author>
			<name>fcamel</name>
					</author>
		<title type="html"><![CDATA[中翻英，線上翻譯測試 － Google Strikes Back！]]></title>
		<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/fcamel/~3/2OmzmY8xqAI/" />
		<id>http://fcamel.twbbs.org/archives/2009/04/22/799/</id>
		<updated>2009-04-22T14:47:06Z</updated>
		<published>2009-04-22T14:47:06Z</published>
		<category scheme="http://fcamel.twbbs.org" term="All" /><category scheme="http://fcamel.twbbs.org" term="English" /><category scheme="http://fcamel.twbbs.org" term="Fun" /><category scheme="http://fcamel.twbbs.org" term="Google" /><category scheme="http://fcamel.twbbs.org" term="中文" />		<summary type="html"><![CDATA[( 這篇寫於 2008-10-26，不知為啥從 Blog 裡消失了，所幸 Google Reader 裡有備份。 )
剛才無聊看看 Google Analytics ，發現本站最熱門的文章還是那篇《中翻英，線上翻譯測試》，我不知該說什麼，這不是件令人高興的事啊。總之，一時興起又來測看看。

最近 Google 翻譯似乎變強了，果然有在學習啊！輸入問題如下：
我要代替月亮來懲罰你
兩年前 Google 的回報令我很失望：
I want to punish you replace Moon
沒想到現在 Google 幾乎翻對了！
I want to punish you in place of the moon
google 一下會發現有不少人提到類似的句子，附帶一提，個人心目中最完美的翻譯是：
In the name of the moon, I will punish you!
其它家翻譯結果沒什麼變，就不再貼了。
另外再測了之前亂測的中文詩詞，有些變化，不過說不上是變好變壞，都很糟就是了。看來博大精測的中文仍是機器翻譯無法跨越的高牆，也是國高中生們無法跨越的牆啦。
]]></summary>
		<content type="html" xml:base="http://fcamel.twbbs.org/archives/2009/04/22/799/">&lt;p&gt;( 這篇寫於 2008-10-26，不知為啥從 Blog 裡消失了，所幸 Google Reader 裡有備份。 )&lt;/p&gt;
&lt;p&gt;剛才無聊看看 &lt;a href="https://www.google.com/analytics/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.google.com');"&gt;Google Analytics &lt;/a&gt;，發現本站最熱門的文章還是那篇&lt;a href="/archives/2006/10/19/114/"&gt;《中翻英，線上翻譯測試》&lt;/a&gt;，我不知該說什麼，這不是件令人高興的事啊。總之，一時興起又來測看看。&lt;/p&gt;
&lt;p&gt;&lt;span id="more-799"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;最近 Google 翻譯似乎變強了，果然有在學習啊！輸入問題如下：&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;我要代替月亮來懲罰你&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;兩年前 Google 的回報令我很失望：&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;I want to punish you replace Moon&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;沒想到現在 &lt;a href="http://translate.google.com/translate_t?hl=zh-TW#zh-CN%7Cen%7C%E6%88%91%E8%A6%81%E4%BB%A3%E6%9B%BF%E6%9C%88%E4%BA%AE%E4%BE%86%E6%87%B2%E7%BD%B0%E4%BD%A0" onclick="javascript:pageTracker._trackPageview('/outbound/article/translate.google.com');"&gt;Google 幾乎翻對了！&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;I want to punish you in place of the moon&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;google 一下會發現有不少人提到類似的句子，附帶一提，個人心目中最完美的翻譯是：&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;In the name of the moon, I will punish you!&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;其它家翻譯結果沒什麼變，就不再貼了。&lt;/p&gt;
&lt;p&gt;另外再測了&lt;a href="http://fcamel.twbbs.org/archives/2006/10/19/115/" &gt;之前亂測的中文詩詞，&lt;/a&gt;有些變化，不過說不上是變好變壞，都很糟就是了。看來博大精測的中文仍是機器翻譯無法跨越的高牆，也是&lt;a href="http://blog.chinatimes.com/sow/Archive/2008/10/14/334349.html" onclick="javascript:pageTracker._trackPageview('/outbound/article/blog.chinatimes.com');"&gt;國高中生們無法跨越的牆啦。&lt;/a&gt;&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/fcamel/~4/2OmzmY8xqAI" height="1" width="1"/&gt;</content>
		<link rel="replies" type="text/html" href="http://fcamel.twbbs.org/archives/2009/04/22/799/#comments" thr:count="1" />
		<link rel="replies" type="application/atom+xml" href="http://fcamel.twbbs.org/archives/2009/04/22/799/feed/atom/" thr:count="1" />
		<thr:total>1</thr:total>
	<feedburner:origLink>http://fcamel.twbbs.org/archives/2009/04/22/799/</feedburner:origLink></entry>
		<entry>
		<author>
			<name>fcamel</name>
					</author>
		<title type="html"><![CDATA[強化 Python 在 Vim 裡的顏色]]></title>
		<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/fcamel/~3/XzBRZldff5Y/" />
		<id>http://fcamel.twbbs.org/archives/2009/04/22/798/</id>
		<updated>2009-04-23T02:02:06Z</updated>
		<published>2009-04-22T13:59:35Z</published>
		<category scheme="http://fcamel.twbbs.org" term="All" /><category scheme="http://fcamel.twbbs.org" term="Programming" /><category scheme="http://fcamel.twbbs.org" term="Python" /><category scheme="http://fcamel.twbbs.org" term="System" /><category scheme="http://fcamel.twbbs.org" term="VIM" />		<summary type="html"><![CDATA[我習慣用 putty 連 Unix server 開 screen，再用 vim 寫 Python。這篇記錄如何改善 Python 的顏色。

啟動 256 色 terminal
首先將可用的色彩數增加為 256 色，先確定 putty 為新版 ( 舊版 putty 沒支援 256 色)。接著參照《讓 Vim、Screen 支援 256 色》將 Screen 和 Vim 設好，記得先用文末提到的 Colortest 測試是否有符合 256 色，成功的話，數字 0 ~ 15 為原本的 16 色，16 ~ 231 為新的顏色 (6 x 6 x 6)，232 ~ 255 為新的灰階色。可配合 Colortest [...]]]></summary>
		<content type="html" xml:base="http://fcamel.twbbs.org/archives/2009/04/22/798/">&lt;p&gt;我習慣用 putty 連 Unix server 開 screen，再用 vim 寫 Python。這篇記錄如何改善 Python 的顏色。&lt;/p&gt;
&lt;p&gt;&lt;span id="more-798"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;啟動 256 色 terminal&lt;/h4&gt;
&lt;p&gt;首先將可用的色彩數增加為 256 色，先確定 putty 為新版 ( 舊版 putty 沒支援 256 色)。接著參照&lt;a href="http://plog.longwin.com.tw/my_note-unix/2008/12/19/vim-screen-set-support-256-color-2008" onclick="javascript:pageTracker._trackPageview('/outbound/article/plog.longwin.com.tw');"&gt;《讓 Vim、Screen 支援 256 色》&lt;/a&gt;將 Screen 和 Vim 設好，記得先用文末提到的 &lt;a href="http://www.vim.org/scripts/script.php?script_id=1349" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.vim.org');"&gt;Colortest&lt;/a&gt; 測試是否有符合 256 色，成功的話，數字 0 ~ 15 為原本的 16 色，16 ~ 231 為新的顏色 (6 x 6 x 6)，232 ~ 255 為新的灰階色。可配合 Colortest 看到數字值和顏色。&lt;/p&gt;
&lt;p&gt;這裡備忘該篇提到的步驟：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在 ~/.vimrc 加入：

&lt;div class="wp_syntax"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="line_numbers"&gt;&lt;pre&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre class="bash" style="font-family:monospace;"&gt;&lt;span style="color: #000000; font-weight: bold;"&gt;set&lt;/span&gt; &lt;span style="color: #007800;"&gt;t_Co&lt;/span&gt;=&lt;span style="color: #000000;"&gt;256&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;在 ~/.screenrc 加入：

&lt;div class="wp_syntax"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="line_numbers"&gt;&lt;pre&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre class="bash" style="font-family:monospace;"&gt;termcapinfo xterm &lt;span style="color: #ff0000;"&gt;'Co#256:AB=\E[48;5;%dm:AF=\E[38;5;%dm'&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;更新 Python script 偵測的格式&lt;/h4&gt;
&lt;p&gt;更新 syntax/python.vim，讓 Vim 偵測出更多種格式，像是行末多的空白，或是空白行有含空格都會被偵測出來。做法如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;下載最新的 &lt;a href="http://www.vim.org/scripts/download_script.php?src_id=9293" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.vim.org');"&gt;python.vim&lt;/a&gt; 並放到 ~/.vim/syntax/ 下。
  &lt;/li&gt;
&lt;li&gt;編輯 ~/.vimrc，加入

&lt;div class="wp_syntax"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="line_numbers"&gt;&lt;pre&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre class="bash" style="font-family:monospace;"&gt;&lt;span style="color: #7a0874; font-weight: bold;"&gt;let&lt;/span&gt; python_highlight_all = &lt;span style="color: #000000;"&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;以顯示所有 python.vim 有偵測的格式。參照 python.vim 開頭的註解，了解細部選項。 &lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;替換顯示的顏色&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;下載 Vim color scheme: &lt;a href="http://www.vim.org/scripts/download_script.php?src_id=9587" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.vim.org');"&gt;Wombat256.vim&lt;/a&gt;，並放到 ~/.vim/colors/ 下。
&lt;/li&gt;
&lt;li&gt;在 ~/.vimrc 裡加入

&lt;div class="wp_syntax"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="line_numbers"&gt;&lt;pre&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre class="bash" style="font-family:monospace;"&gt;colorscheme wombat256&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如此一來就有漂亮的顏色啦，wombat256 的 screenshot 見&lt;a href="http://dengmao.wordpress.com/2007/01/22/vim-color-scheme-wombat/" onclick="javascript:pageTracker._trackPageview('/outbound/article/dengmao.wordpress.com');"&gt;這裡&lt;/a&gt;。&lt;/p&gt;
&lt;h4&gt;備註&lt;/h4&gt;
&lt;p&gt;&lt;a href="http://www.cs.cmu.edu/~maverick/VimColorSchemeTest/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.cs.cmu.edu');"&gt;這個網站&lt;/a&gt;有提供許多不同的 color scheme screenshot，可惜沒附 Python 的 screenshot。&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/fcamel/~4/XzBRZldff5Y" height="1" width="1"/&gt;</content>
		<link rel="replies" type="text/html" href="http://fcamel.twbbs.org/archives/2009/04/22/798/#comments" thr:count="4" />
		<link rel="replies" type="application/atom+xml" href="http://fcamel.twbbs.org/archives/2009/04/22/798/feed/atom/" thr:count="4" />
		<thr:total>4</thr:total>
	<feedburner:origLink>http://fcamel.twbbs.org/archives/2009/04/22/798/</feedburner:origLink></entry>
		<entry>
		<author>
			<name>fcamel</name>
					</author>
		<title type="html"><![CDATA[最近用 Python + TDD 心得（與 Java 做對照）]]></title>
		<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/fcamel/~3/tIW9OkgvcfA/" />
		<id>http://fcamel.twbbs.org/archives/2009/04/19/784/</id>
		<updated>2009-04-20T09:24:22Z</updated>
		<published>2009-04-19T15:46:12Z</published>
		<category scheme="http://fcamel.twbbs.org" term="Programming" /><category scheme="http://fcamel.twbbs.org" term="Python" /><category scheme="http://fcamel.twbbs.org" term="Refactoring" /><category scheme="http://fcamel.twbbs.org" term="Testing" />		<summary type="html"><![CDATA[ 最近又用 Python 寫了些程式，剛好和之前用 Java 進行 TDD 做個對照。

若不知道 TDD 的人，可以先參考這篇，TDD 的概念是依以下三個步驟寫程式 ( 順序相當重要 )：

先寫簡單的單元測試，並執行它。
用最簡單的方法實作需要的功能，讓程式能通過測試。
重構程式，並確保重構後的程式仍能通過測試。

實作部份不需多談，這裡先分別對步驟一「寫測試碼」和步驟三「重構」討論，再分享一般性的一點心得。
寫測試碼
Python 寫起測試碼比 Java 簡單許多，可以輕易地抽換所有既有物件，如 module、function、class、method。下面是一個簡單的抽換 method 的例子：

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyClass&#40;object&#41;:
    def hello&#40;self, msg&#41;:
        print msg
&#160;
def new_hello&#40;self, msg&#41;:
    print msg + &#34; (by replaced method)&#34;
&#160;
obj = MyClass&#40;&#41;
obj.hello&#40;'Testing rocks!'&#41;  # Testing rocks!
mystub [...]]]></summary>
		<content type="html" xml:base="http://fcamel.twbbs.org/archives/2009/04/19/784/">&lt;p&gt; 最近又用 &lt;a href="http://www.python.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.python.org');"&gt;Python&lt;/a&gt; 寫了些程式，剛好和之前用 Java 進行 &lt;a href="http://en.wikipedia.org/wiki/Test-driven_developmen" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;TDD &lt;/a&gt;做個對照。&lt;/p&gt;
&lt;p&gt;&lt;span id="more-784"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;若不知道 TDD 的人，可以先參考&lt;a href="http://fcamel.twbbs.org/archives/2009/03/09/747/"  title="/archives/2009/03/09/747/"&gt;這篇&lt;/a&gt;，TDD 的概念是依以下三個步驟寫程式 ( &lt;strong&gt;順序相當重要&lt;/strong&gt; )：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先寫簡單的單元測試，並執行它。&lt;/li&gt;
&lt;li&gt;用最簡單的方法實作需要的功能，讓程式能通過測試。&lt;/li&gt;
&lt;li&gt;重構程式，並確保重構後的程式仍能通過測試。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;實作部份不需多談，這裡先分別對步驟一「寫測試碼」和步驟三「重構」討論，再分享一般性的一點心得。&lt;/p&gt;
&lt;h4&gt;寫測試碼&lt;/h4&gt;
&lt;p&gt;Python 寫起測試碼比 Java 簡單許多，可以&lt;strong&gt;輕易地抽換所有既有物件&lt;/strong&gt;，如 module、function、class、method。下面是一個簡單的抽換 method 的例子：&lt;/p&gt;

&lt;div class="wp_syntax"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="line_numbers"&gt;&lt;pre&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre class="python" style="font-family:monospace;"&gt;&lt;span style="color: #ff7700;font-weight:bold;"&gt;class&lt;/span&gt; MyClass&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;&lt;span style="color: #008000;"&gt;object&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;:
    &lt;span style="color: #ff7700;font-weight:bold;"&gt;def&lt;/span&gt; hello&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;&lt;span style="color: #008000;"&gt;self&lt;/span&gt;, msg&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;:
        &lt;span style="color: #ff7700;font-weight:bold;"&gt;print&lt;/span&gt; msg
&amp;nbsp;
&lt;span style="color: #ff7700;font-weight:bold;"&gt;def&lt;/span&gt; new_hello&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;&lt;span style="color: #008000;"&gt;self&lt;/span&gt;, msg&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;:
    &lt;span style="color: #ff7700;font-weight:bold;"&gt;print&lt;/span&gt; msg + &lt;span style="color: #483d8b;"&gt;&amp;quot; (by replaced method)&amp;quot;&lt;/span&gt;
&amp;nbsp;
obj = MyClass&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;
obj.&lt;span style="color: black;"&gt;hello&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;&lt;span style="color: #483d8b;"&gt;'Testing rocks!'&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;  &lt;span style="color: #808080; font-style: italic;"&gt;# Testing rocks!&lt;/span&gt;
mystub = stub.&lt;span style="color: black;"&gt;Stub&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;
mystub.&lt;span style="color: black;"&gt;replace&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;MyClass, &lt;span style="color: #483d8b;"&gt;'hello'&lt;/span&gt;, new_hello&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;
obj.&lt;span style="color: black;"&gt;hello&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;&lt;span style="color: #483d8b;"&gt;'Testing rocks!'&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;  &lt;span style="color: #808080; font-style: italic;"&gt;# Testing rocks! (by replaced method)&lt;/span&gt;
mystub.&lt;span style="color: black;"&gt;restore&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;MyClass, &lt;span style="color: #483d8b;"&gt;'hello'&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;
obj.&lt;span style="color: black;"&gt;hello&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#40;&lt;/span&gt;&lt;span style="color: #483d8b;"&gt;'Testing rocks!'&lt;/span&gt;&lt;span style="color: black;"&gt;&amp;#41;&lt;/span&gt;  &lt;span style="color: #808080; font-style: italic;"&gt;# Testing rocks!&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;註解表示該行輸出結果，其中 Stub 是我寫得一個簡單 class，只有十行多，可以替換和還原物件。可以從 MyClass.hello() 的輸出看出，stub.replace() 後 MyClass.hello() 被換為 new_hello()，stub.restore() 後則換了回來。完整的程式碼請看&lt;a href="http://pastie.textmate.org/451440" onclick="javascript:pageTracker._trackPageview('/outbound/article/pastie.textmate.org');"&gt;這裡&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;方便抽換物件有什麼好處呢？舉例來說，若 class A 有兩個 method B 和 C，其中 B 用到 C ( B 會視 C 傳回結果改變程式流程 )。&lt;strong&gt;為了方便測試 B 的行為，得先控制 C 的傳回結果。&lt;/strong&gt;若是 Python 的話，照上面的例子透過 Stub ( setattr() ) 替換 A.C 即可。但在 Java 的情況，除非將 C 當作參數傳給 B，讓測試程式有機會傳「假的 C 」給 B，不然難以控制 B 內部行為。寫新程式時還有機會改設計，對於舊的程式，修改程式是個災難。用到別人的函式庫或沒程式碼就無解了。不論 Java 有何解法 ( 如採用 &lt;a href="http://en.wikipedia.org/wiki/Dependency_injection" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;injection&lt;/a&gt; 的方式 )，&lt;strong&gt;解法愈麻煩，表示大眾愈不願意做，因此降低測試碼的品質，連帶影響 TDD 效果。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.martinfowler.com/articles/mocksArentStubs.html " onclick="javascript:pageTracker._trackPageview('/outbound/article/www.martinfowler.com');"&gt;Mock &lt;/a&gt;library 部份，Java 有 &lt;a href="http://easymock.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/easymock.org');"&gt;EasyMock&lt;/a&gt;，Python 有 &lt;a href="http://code.google.com/p/pymox/" onclick="javascript:pageTracker._trackPageview('/outbound/article/code.google.com');"&gt;Mox&lt;/a&gt;，兩者用法差不多。後者是前者改來的，學一套兩邊都可以用。&lt;/p&gt;
&lt;h4&gt;重構&lt;/h4&gt;
&lt;p&gt;由於 Java 的特性 ( &lt;a href="http://en.wikipedia.org/wiki/Type_system" onclick="javascript:pageTracker._trackPageview('/outbound/article/en.wikipedia.org');"&gt;static type&lt;/a&gt; )，重構工具相當成熟，我慣用 &lt;a href="http://www.eclipse.org/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.eclipse.org');"&gt;Eclipse&lt;/a&gt; 的重構功能，離開它就不太想寫 Java。其中我最常用的功能是改名稱，包含 variable、method 、 class、 package 等。&lt;strong&gt;好的名稱是好程式碼的必要條件之一&lt;/strong&gt;，好的名稱可以省去冗長的註解、縮短理解程式的時間，同時也有助於作者釐清邏輯，減少犯錯的機會。若發現名稱很難取，也許表示該 object（method / class / package）功能沒規範好，之後容易遇到問題。寫程式難免會犯錯，寫一段時間後才發現命名不夠精確，得回頭修改。&lt;strong&gt;若有好的重構工具，改起來快又沒風險，可以提高重構的意願。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;反觀 Python 因為 dynamic type，程式碼本身提供的訊息不足（得執行後才清楚全貌），難以透過工具重構。我找了一下相關工具後沒看到滿意的。Eclipse 的 &lt;a href="http://pydev.sourceforge.net/" onclick="javascript:pageTracker._trackPageview('/outbound/article/pydev.sourceforge.net');"&gt;PyDev&lt;/a&gt; plugin 有提供重構，但試用後結果是錯的，改 module 名稱時沒動到檔名，執行後才會發現程式碼爛了。另外試了 &lt;a href="http://rope.sourceforge.net/" onclick="javascript:pageTracker._trackPageview('/outbound/article/rope.sourceforge.net');"&gt;Rope&lt;/a&gt; ，可以正確改名，可是速度有點慢，操作相當不直覺 ( 之後再來試看看 &lt;a href="http://rope.sourceforge.net/ropevim.html" onclick="javascript:pageTracker._trackPageview('/outbound/article/rope.sourceforge.net');"&gt;ropevim&lt;/a&gt; )。為了長遠發展考量，或許可以試看看自己弄個簡單版的改名工具或改 Rope，工具的功能可以少，但要快、容易操作且結果正確。&lt;/p&gt;
&lt;h4&gt;結語&lt;/h4&gt;
&lt;p&gt;只有在三個步驟都確實做到時，TDD 才能發揮應有的威力。依我目前一點點的實作心得，Python 容易寫測試碼、卻不方便重構；而 Java 正好相反。現在我大多有照 TDD 的流程寫程式，即使寫個一小時的小程式，也會用 TDD 。有時寫寫覺得卡卡的，才發現忘了先寫測試碼。&lt;/p&gt;
&lt;p&gt;另外，&lt;strong&gt;測試碼的範圍抓得準 ( 別測太細，也別懶得測核心) ，效果才會好。&lt;/strong&gt;我初用 TDD + Python 時就矯枉過正，寫太多測試變成 「over-testing」，省了 over-design 的負擔，卻多花時間寫不必要的測試碼、又增加日後維護的成本 。&lt;/p&gt;
&lt;p&gt;養成 TDD 習慣的關鍵，在於寫測試碼的功力，像是如何準備 fixture ，如何改善函式介面以利測試。&lt;strong&gt;為了寫測試而改變函式介面並不是本末倒置&lt;/strong&gt;，通常這會降低函式之間的關聯性，將功能明確切開，讓每個函式的輸入輸出都很乾淨（有簡單的輸入，才方便準備 fixture）。而乾淨的輸入輸出意味著函式更容易被組合使用。&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/fcamel/~4/tIW9OkgvcfA" height="1" width="1"/&gt;</content>
		<link rel="replies" type="text/html" href="http://fcamel.twbbs.org/archives/2009/04/19/784/#comments" thr:count="0" />
		<link rel="replies" type="application/atom+xml" href="http://fcamel.twbbs.org/archives/2009/04/19/784/feed/atom/" thr:count="0" />
		<thr:total>0</thr:total>
	<feedburner:origLink>http://fcamel.twbbs.org/archives/2009/04/19/784/</feedburner:origLink></entry>
		<entry>
		<author>
			<name>fcamel</name>
					</author>
		<title type="html"><![CDATA[軟體開發技巧的待讀書單]]></title>
		<link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/fcamel/~3/_1ISHWzX9vs/" />
		<id>http://fcamel.twbbs.org/archives/2009/03/15/780/</id>
		<updated>2009-03-15T05:12:16Z</updated>
		<published>2009-03-15T05:05:44Z</published>
		<category scheme="http://fcamel.twbbs.org" term="All" /><category scheme="http://fcamel.twbbs.org" term="Java" /><category scheme="http://fcamel.twbbs.org" term="Programming" /><category scheme="http://fcamel.twbbs.org" term="Refactoring" /><category scheme="http://fcamel.twbbs.org" term="Software Engineering" /><category scheme="http://fcamel.twbbs.org" term="Testing" />		<summary type="html"><![CDATA[經過這陣子的評估，終於列出了夢幻般 (?) 的讀書清單。我的做法是先在網上看到有人推薦，到書局翻一陣子，再回來查 Amazon 的評論，最後決定是否有必要看。附帶一提，待這書單決定後又查了一下，結果發現每本書都有得到 Jolt Award，一瞬間好像以為 Jolt Award 不值錢了 :-)。

以下依暫定的閱讀順序依序說明，除前兩本外，後三本都有中文版： 
1. Test Driven Development by Examples
Amazon 評論 4 顆星，由 Kent Beck 所著。全書只有 200+ 頁，用大量的例子，一步一步說明怎麼進行 TDD，相當易讀。這裡有別人寫的詳細書評。
2. xUnit Test Patterns: Refactoring Test Code
Amazon 評論 4.5 顆星，不過只有七筆評論，樣本略嫌不足。 在書店試翻的感想是：好書但不易讀，而且實在是太厚了。ThoughtWorks 專出這種書嗎？
3. Refactoring to Patterns
Amazon 評論 4 顆星，但評論兩極化落在 3 和 5，最中肯的評論為： &#34;Good ideas, but needs refactoring&#34;，值得一讀，但不好消化。讀過 Martin Fowler 的 Refactoring [...]]]></summary>
		<content type="html" xml:base="http://fcamel.twbbs.org/archives/2009/03/15/780/">&lt;p&gt;經過這陣子的評估，終於列出了夢幻般 (?) 的讀書清單。我的做法是先在網上看到有人推薦，到書局翻一陣子，再回來查 Amazon 的評論，最後決定是否有必要看。附帶一提，待這書單決定後又查了一下，結果發現每本書都有得到 &lt;a href="http://www.joltawards.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.joltawards.com');"&gt;Jolt Award&lt;/a&gt;，一瞬間好像以為 Jolt Award 不值錢了 :-)。&lt;/p&gt;
&lt;p&gt;&lt;span id="more-780"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;以下依暫定的閱讀順序依序說明，除前兩本外，後三本都有中文版： &lt;/p&gt;
&lt;h4&gt;1. Test Driven Development by Examples&lt;/h4&gt;
&lt;p&gt;&lt;a href="http://www.amazon.com/Test-Driven-Development-Addison-Wesley-Signature/dp/0321146530" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.amazon.com');"&gt;Amazon 評論 4 顆星&lt;/a&gt;，由 Kent Beck 所著。全書只有 200+ 頁，用大量的例子，一步一步說明怎麼進行 TDD，相當易讀。這裡有別人寫的&lt;a href="http://www.kenming.idv.tw/index.php?title=ithome_a_cec_a_11_test_driven_developmen&amp;amp;more=1&amp;amp;c=1&amp;amp;tb=1&amp;amp;pb=1" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.kenming.idv.tw');"&gt;詳細書評&lt;/a&gt;。&lt;/p&gt;
&lt;h4&gt;2. xUnit Test Patterns: Refactoring Test Code&lt;/h4&gt;
&lt;p&gt;&lt;a href="http://www.amazon.com/xUnit-Test-Patterns-Refactoring-Addison-Wesley/dp/0131495054" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.amazon.com');"&gt;Amazon 評論 4.5 顆星&lt;/a&gt;，不過只有七筆評論，樣本略嫌不足。 在書店試翻的感想是：好書但不易讀，而且實在是太厚了。&lt;a href="http://www.thoughtworks.com/" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.thoughtworks.com');"&gt;ThoughtWorks&lt;/a&gt; 專出這種書嗎？&lt;/p&gt;
&lt;h4&gt;3. Refactoring to Patterns&lt;/h4&gt;
&lt;p&gt;&lt;a href="http://www.amazon.com/Refactoring-Patterns-Addison-Wesley-Signature-Kerievsky/dp/0321213351" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.amazon.com');"&gt;Amazon 評論 4 顆星&lt;/a&gt;，但評論兩極化落在 3 和 5，最中肯的評論為： &amp;quot;Good ideas, but needs refactoring&amp;quot;，值得一讀，但不好消化。讀過 Martin Fowler 的 &lt;a href="http://www.books.com.tw/exep/prod/booksfile.php?item=0010411649" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.books.com.tw');"&gt;Refactoring&lt;/a&gt; 並有一段實戰經驗後的最佳書藉。本書中文版&lt;a href="http://www.books.com.tw/exep/prod/booksfile.php?item=0010399556" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.books.com.tw');"&gt;《重構－向範式前進》&lt;/a&gt;，且是侯捷合譯的 。 &lt;/p&gt;
&lt;h4&gt;4. Head First - Head First Object-Oriented Analysis and Design&lt;/h4&gt;
&lt;p&gt;&lt;a href="http://www.amazon.com/Head-First-Object-Oriented-Analysis-Design/dp/0596008678" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.amazon.com');"&gt;Amazon 評論 4 顆星&lt;/a&gt;，評論裡指出本書適合初學者，另外&lt;a href="http://www.amazon.com/review/R3E3M5KIN2NYA1/ref=cm_cr_pr_cmt?ie=UTF8&amp;amp;ASIN=0596008678&amp;amp;nodeID=#wasThisHelpful" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.amazon.com');"&gt;最多人同意的評論 (Decent Introduction to OOA&amp;amp;D) &lt;/a&gt;指出：本書不夠簡潔，並有不少小錯，若有第二版才值得推薦。&amp;#160;&lt;br /&gt;
&lt;h4&gt;5. Head First - Design Pattern&lt;/h4&gt;
&lt;p&gt;&lt;a href="http://www.amazon.com/First-Design-Patterns-Elisabeth-Freeman/dp/0596007124" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.amazon.com');"&gt;Amazon 評論 4.5 顆星&lt;/a&gt;，且是壓倒性的一堆 5 顆星。Head First 的書以易讀出名，但通常也寫得很厚，需要花不少時間消化 。 對照過於精簡典雅的 &lt;a href="http://www.amazon.com/Design-Patterns-Object-Oriented-Addison-Wesley-Professional/dp/0201633612" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.amazon.com');"&gt;Design Pattern Bible Book&lt;/a&gt;，這本書親切不少。附帶一提，在書店還有看到 &lt;a href="http://www.amazon.com/Head-First-Algebra-Learners-Brain-Friendly/dp/0596514867" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.amazon.com');"&gt;Head First - Algebra&lt;/a&gt;，真是太有趣了！可惜是教國中代數，不是教線性代數。&lt;/p&gt;
&lt;p&gt;依目前心得來說，有四件事要學：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;寫出良好的測試程式 - book 2 &lt;/li&gt;
&lt;li&gt;寫出良好的 OOP- book 4, 5 &lt;/li&gt;
&lt;li&gt;提昇重構技巧 - book 3 &lt;/li&gt;
&lt;li&gt;TDD (其實就是上述三者的綜合體) - book 1 &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;前往軟體開發聖殿之路是很遠的，希望一個月至少能解決一本，並持續地應用到實戰中，半年後就出師啦！ &lt;/p&gt;
&lt;h4&gt;備註&lt;/h4&gt;
&lt;p&gt;這裡補充我曾看過且大力推薦的書藉：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.books.com.tw/exep/prod/booksfile.php?item=0010233987" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.books.com.tw');"&gt;Effective Java Programming Language Guide&lt;/a&gt;：Joshua Bloch 所著。中文版只有第一版，但原文已有第二版，我只看過中譯第一版。本書適合偶而拿出來重習學習，薄又易讀。若不認識 Joshua Bloch，至少也應該用過他做的 &lt;a href="http://java.sun.com/docs/books/tutorial/collections/index.html" onclick="javascript:pageTracker._trackPageview('/outbound/article/java.sun.com');"&gt;Java Collections Framework&lt;/a&gt;。 &lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.books.com.tw/exep/prod/booksfile.php?item=0010411649" onclick="javascript:pageTracker._trackPageview('/outbound/article/www.books.com.tw');"&gt;重構：改善既有程式的設計&lt;/a&gt;：Martin Fowler 所著，淺顯易懂的好書。說明何謂壞程式碼，以及如何一小步一小步安全地修正壞程式碼。&lt;/li&gt;
&lt;/ul&gt;
&lt;img src="http://feeds.feedburner.com/~r/fcamel/~4/_1ISHWzX9vs" height="1" width="1"/&gt;</content>
		<link rel="replies" type="text/html" href="http://fcamel.twbbs.org/archives/2009/03/15/780/#comments" thr:count="0" />
		<link rel="replies" type="application/atom+xml" href="http://fcamel.twbbs.org/archives/2009/03/15/780/feed/atom/" thr:count="0" />
		<thr:total>0</thr:total>
	<feedburner:origLink>http://fcamel.twbbs.org/archives/2009/03/15/780/</feedburner:origLink></entry>
	</feed>
