#導覽列 #文章列表 #許洛豪
在光之翼的守護下訴說著一段段的英雄傳說。 [ 首頁 | 網誌 | 相簿 | 留言 | 訂閱 ]

『將手機宅化以侵略藍星』作戰計劃第一彈!

此文評價一顆星二顆星三顆星四顆星五顆星
Loading ... Loading ...
主人要記得寫標題喔!主人要記得寫標題喔!
主人要記得寫標題喔!(View at PicasaWeb)

話說這幾天都在搞 MaidroidGTD 這隻程式,遇寫愈覺得 Android 根本就是一整個犯規嘛!幾乎什麼都可以改,什麼都可以惡搞,而且方法還很簡單,簡直就是在鼓勵人們來惡搞嘛!而且改不出來的時候還可以直接挖原始碼出來看,這真是太邪惡了!

愈來愈期待將來 Android  會怎麼樣改變人們對於手機和行動裝置的想像。

回歸正題,這兩天一邊看著官方網站的說明文件,一邊看著星期四去買的這一本『Android SDK 開發範例大全』,把 MaidroidGTD  的收件匣的部份給弄好囉。

目前還很陽春,就是一般資料庫的增刪查改而已,然後再加上看起來好像還不夠萌的萌化措施。

簡而言之,這個程式的核心出發點很簡單:用萌化以及對話的方式,讓 GTD 的管理流程變得有趣,然後會吸引我自己去用。

最終的目的是讓這隻程式跑在將來我買的 Android 實體手機上,然後讓 GTD 的管理模式融入自己的生活中。(謎之音:其實這家伙內心想的最終的目的是看能不能把他放到 Android Market 上撈上一筆,或是看趕不趕得及參加第二屆的 Android 應用軟體大賞來出個名。)

這就是『將手機宅化以侵略藍星』作戰計劃的第一步!

如果這隻程式能順利完成,我想第二彈應該會是萌化的計帳軟體,理由一樣,我試過很多記帳方式,不過都沒有持之以恆的動力啊!我要靠把手機萌化來拯救自己的生活!(謎之音:這樣應該只會愈陷愈深吧?)

順道一提,有沒有人會用模擬器內附的谷歌拼音輸入法啊?我怎麼按就是按不出來。>_<

將本文加入 Hemidemi 書籤
Brian Hsu (墳墓)
2009-05-31 (週日) 11:11:11

[Haskell] 真的比較好懂和不容易出錯嗎?

此文評價一顆星二顆星三顆星四顆星五顆星
Loading ... Loading ...

話說在 Haskell 的官方網站以及教學文件中,都提到了 Haskell 的好處之一是能寫出 Bug 比較少、比較短、比較清楚的程式。

在 Real World Haskell 中也提到下面這樣的觀點:

Even with years of experience, we remain astonished and pleased by how often our Haskell programs simply work on the first try, once we fix those compilation errors.

雖然我目前只讀到那本書的第三章,關於函式的定義和使用的部份,但做了一些練習題後,其實我有些懷疑 Functional Programming 社群這樣子的宣稱,是不是有點言過其實。

在接下來,我會拿 Real World Haskell 裡一個實際的練習題,說明我為什麼會有這樣子的想法。

首先,我們來看書中這個練習題的題目:

Write a function that computes the mean of a list, i.e. the sum of all elements in the list divided by its length.

在 Haskell 裡的 List,其實就等於是陣列,因此這個題目很簡單:寫一個函式計算陣列的平均值。我想這個程式應該很簡單,任何宣稱自己會寫程式的人應該在三分鐘之內就能把程式寫好,並跑出正確的結果才是。

OK,為了再突顯出問題的所在,我們把問題再簡化一點,要求出陣列元素的平均,我們要先求陣列裡每個元素的加總。

所以,我們先把問題改成:寫一個函數,計算陣列裡所有元素加總後的數值。

好,首先我們先來看 D 語言是怎麼做的,我想這也是大部份的人一看到這個題目,第一個想到的方法。

  1. int sumD (int [] list)
  2. {
  3.     int sum = 0;
  4.     foreach (int element; list) {
  5.         sum = sum + eelemnt;
  6.     }
  7.     return sum;
  8. }

相當標準的做法,而且我相信即使你沒學過 D 語言,只要有 C-Syntax 程式與言寫作的經驗,應該也猜得出來那個 foreach 迴圈是在做什麼用的。

好,接著我們再來在看 Haskell 裡,我們應該要怎麼寫這隻程式。

  1. sumHaskell xs | null xs   = 0
  2.               | otherwise =
  3.                     (head xs) +
  4.                     sumHaskell (tail xs)

什麼?沒學過 Haskell 看不懂?沒關係,我們把這個程式改寫成 D 語言的版本,但不去更改它的演算法,就會變成下面這個樣子:

  1. int sumHaskell (int [] list)
  2. {
  3.     if (list.length == 0) {
  4.         return 0;
  5.     }
  6.  
  7.     return list[0] +
  8.            sumHaskell (list[1..list.length]);
  9. }

WOW!我們竟然沒有宣告任何存放結果的變數,就可以達到和之前的程式相同的效果,多神奇啊!

可是先等等,為什麼我們弄兩份不同的程式碼出來?不能直接用第一個版本走遍天下嗎?

喔喔,抱歉,不行,因為在 Haskell 裡有下面兩個特性,讓你不可能實做出第一個版本的程式:

  1. 所有的變數都是常數,在第一次定義後就不能變動,因此你不可能用一個變數來累加。
  2. 根本沒有迴圈。

有了以上兩個限制,我們在 Haskell 裡,只能實做出第二個版本,而第二個版本……遞迴!

於是爭論就開始了,第二個版本真的比較簡單、清楚,又不容易出錯嗎?很抱歉,我並不這樣想。

在第一個版本裡,我們的處理邏輯很簡單,宣告一個變數存放最終的加總值,用一個迴圈依序取出陣列裡的每一個元素,並一直累加到之前宣告的變數裡。

我相信你給任何一個小學生一個不規則數列,請他們在紙上加總,高達八成的小學生,會利用以上的方法來做這個題目,我們很乖的把第一二個數字相加,再加上第三個數字,再加上第四個,再加……

那麼 Haskell 的版本呢?遞迴!看一下程式碼,嗯,有一個 list[0],所以我們先把陣列的第一個元素拿出來,加上後面那一串。

呃,後面那一串用一個方法處理了 list[1..list.length],也就是陣列裡剩下的部份。

OK,所以我們也是從陣列的第一個元素開始算,再加第二個元素,再加第三個……

叭噗!錯錯錯!完全錯!在第二隻程式裡,我們實際上是從陣列的最後一個元素開始加總,在 [3, 5, 7, 9] 這樣的陣列裡,我們的實際計算過程如下:

  1. 9 + 0 = 9
  2. 7 + 9 = 16
  3. 5 + 16 = 21
  4. 3 + 21 = 24

看到了嗎?我們寫出的程式是和直覺相反的!在這樣的情況下,你要怎麼宣稱你寫的程式是簡潔、易懂、好維護又不容易出錯呢?

確實,我們的第二個版本比第一個版本簡潔多了,但是真的比較容易理解和不會出錯嗎?我們第一個版本的做法,我相信任何一個小學生都能夠理解其中的道理,並且能用紙筆重覆出所有的步驟。

但第二個程式呢?你要怎麼向小學生解釋遞迴的概念,還有為什麼實際跑的時候,我們會從 9 + 0 開始算,而那個 0 又是怎麼來的?

一個無法讓小學生理解的計算方法,真的可以算是容易懂嗎?一段看了之後還得去想為什麼要這樣寫的,會發生什麼事的程式碼,真的比較容易維護嗎?過了三個月之後,你再回頭看這段程式碼,是不是還能夠在三秒內反應過來他整個的運算過程,並且確定他是對的?

在我看來,程式會出錯不外乎兩個原因:

  1. 由語法的小錯誤造成程式結果的錯誤。
  2. 演算法本身的邏輯就有問題。

第一點我們在 C 語言之中就常見到,譬如在實做第一個版本時,for 迴圈的邊際條件設錯,造成陣列索引超出範圍或過小,而導至程式當掉或計算結果錯誤。

這是與法上的錯誤造成的,而不是演算法的錯誤。

確實,Haskell 是一個很嚴厲的語言,語法有一點小錯誤或沒有對好,甚或是型態錯誤,編譯器就一直發出抱怨,叫你要修改程式,否則不給你執行。

但其他非 Functional Programming 典範,比較新的程式語言,也同樣可以避免這類的錯誤。舉例而言,在第一個版本裡,我們用了 D 語言裡頭的 foreach 迴圈,確保我們一定可以完整地走完整個陣列,而且不會超過邊界。

這樣一來,兩個版本在第一點上就沒有什麼差異了,因為不論是 Haskell 或者 D 語言,在設計上都讓你盡量避免第一點的錯誤。

那麼第二點呢?在兩個版本裡頭,我們用的演算法相當不同。第一個版本是典型的 Imperative Programming 的做法,我們清楚的告訴編譯器我們要怎麼完成這件工作--從數列開頭,一個個數字拿出來,並且再把他們加到另一旁已經計算好的數字上。

三句話,小學生都能懂的事。

然而第二個版本呢?遞迴,可怕的遞迴,約耳談軟體裡說可以砍掉一大半的學生的遞迴,程設考卷上永遠有人寫不出來最後結果的遞迴,以及講了半天還是沒啥人懂的河內塔演算法。

這還只是給學生一個遞迴的程式問你是什麼意思時會發生的事,還沒叫他們設計遞迴演算法呢!

邊界條件是什麼?化約方式又是什麼?要怎樣才能確保我的遞迴程式是正確的,不會限入無止境的 Stack Overflow 或是 Segmentation Fault ?

如果你的程式語言,只允許使用遞迴來解大部份的問題,你要怎麼大聲的對那些新手,或是少了遞迴的那根筋的學生說:用這個語言,你寫出來的程式比較容易懂和不容易出錯?

因為在我看來,沒有任何一個使用遞迴的演算法是易懂的,也沒有任何一隻遞迴的程式是不容易出錯的--為什麼第二隻程式我們要 return 0 ,難道 1 不行嗎?助教。

Update (2008/10/01):

Thinker 提供了另一個 Haskell 的版本,我把它稍作整理和同樣改寫成 D 語言的版本如下:

  1. sumHaskell v lst | null lst = v
  2.                  | otherwise =
  3.                        sumHaskell
  4.                            (v + (head lst))  
  5.                            (tail lst)
  1. int sumHaskell2 (int v, int [] list)
  2. {
  3.     if (list.length == 0) {
  4.         return v;
  5.     }
  6.  
  7.     return sumHaskell2 (
  8.         v + list[0],
  9.         list[1..list.length]
  10.     );
  11. }

這次我先不解釋這隻程式到底在幹嘛,也不加註解,請各位自行推測看看。

我個人覺得這個版本比之前的還難解釋(我想我真的是沒有遞迴那根筋的那種人),因為變數更多了,看起來更奇怪了。到底我們第一次呼叫這個函式要計算的時候,v 要傳什麼進去才對呢?後面的化約條件又是為什麼要那樣寫?v 在整個函式裡到底扮演什麼樣的角色?

在第一個遞迴版本中,我還可以想到用一句話來解釋:每一次的運算,我們都先算完後面陣列的總合,再加上我們目前所在的原素,sumHaskell (list[1..list.length]) 會幫我們算後面的陣列總合,所以我們可以不用理它。

如果不去深究遞迴的計算方法,這個解釋還是很合理的--我們先用一個函式計算完 list[1..list.length] 這個陣列,也就是去掉第一個元素後剩下的總合,再加上目前的這個元素,就會是全部的總合。

可是第二個版本,我究竟要怎樣解釋才講的清楚呢?我是在什麼時候做最後的加總的?計算的方法又是什麼?為什麼 list 空的時候要返回 v?我第一次呼叫這個函數的時候 v 要填什麼?

另外,SayYa 上的 letoh 版友提到:

在 D 語言版本的程式也有一行 int sum = 0; 難道 sum = 1 不行嗎?
這是一樣的問題 我能想到的簡單解釋 就是加法單位元素

也許你會有機會看到實作 factorial 範例 為什麼裡面的初始值是 1 不是 0?
我想得到的答案也是一樣 因為要設成乘法單位元素作為終止

我相信這和遞迴好不好懂應該無關:-)

的確,我在寫這篇的時候,並沒有想到第一個版本也有 int sum = 0 的這個問題,但我事後想了一想,我會忽略 int sum = 0 和特別挑出 return 0 的原因,是兩個敘述對我而言,在解釋的難易度上會有差別,而且我最後用的問句也不完整。

int sum = 0;
很明顯的,這是我們總計的結果,當然是從零開始加。
return 0;
助教,為什麼會有兩個 return,前面這個 return 是幹嘛用的?為什麼後面要 0,用 1 不行嗎?

第一個範例裡,我們很明顯的可以看出 sum 是做什麼的,進而推論到 0 是合理的預設值,可是第二個 return 如果對遞迴不熟,就很難去解釋『這行到底要幹嘛』(因為你必須解釋遞迴到最後一層的時候,會用到這個值,所以他等於是一開始的預設值 <== 好繞口)。

我想這是解釋難度上的差別,因為第一個範例實在太好解釋了,那個零有很大的機會不用去解釋。我自己的經驗是,大部份的學生只要知道我們的迴圈是『從頭一個個加』,就不會對於那個 int sum = 0 感到意外。

可是第二個就好難解釋啊,我每次光是解釋為什麼要設邊際條件,和邊際條件的 return 值就累死了。XD

最後順到一提,其實初學 Haskell 到現在,他最讓我感到激賞的是 Type Inference / Type Class,你可以不用宣告函式的標頭,就能夠擁有強型態檢查。而且如果你沒宣告,同一個函式就可以用在各種合理的型態上,成為 Generic 程式(像上面的 Haskell 版本,可以用在整數陣列、浮點數陣列都沒問題),不像 C++/Java/D 還要用 Template 來達成(上面的 D 語言版本只能用在整數陣列)。

這個在寫小程式的時候真的很方便。:p

將本文加入 Hemidemi 書籤
Brian Hsu (墳墓)
2008-09-30 (週二) 19:36:31

[Haskell] 序言。

此文評價一顆星二顆星三顆星四顆星五顆星
Loading ... Loading ...
就是這本書誤了我就是這本書誤了我
就是這本書誤了我(View at PicasaWeb)

先聲明,我還是個函數式程式設計 (Functional Programming) 的新手,而且對於使用這種 Paradigm 的程式語言我也只認識 Haskell 而已,因此這個系列裡有很多想法可能只是我的偏見、誤解與錯覺,所以如果我講錯了,就還請多多包涵。

對 Functional Prgoramming 起興趣,是在很久以前在約耳談軟體系列中看到『爪哇學校的危害』一文,裡頭談到了這種程式設計典範,說了下面這句話:

In a purely functional program, the value of a variable never changes, and yet, it changes all the time! A paradox!

想當然爾,完全沒接處過 Functional Programming 的我,腦海中冒出的第一個念頭就是:變數不能改動,那是要玩什麼啊!

之後在網路上找了一下 Functional Programming 的資料,就把目標鎖定在 Haskell 這個看來不錯的程式語言,而且也到學校的圖書館找了一本英文的教科書來看(中文的根本沒有)。

但不幸的是,讀了半天,我還是搞不懂 Functional Programming 到底是什麼,有什麼好處,因為教科書上的程式碼範例和思考邏輯,都和我自己在寫的程式沒什麼兩樣。

舉例而言,想要把一個圖形給黑白轉的時候,就寫一個函式,參數是原先的圖形,反回值是新的圖形,函式的內容是把每一個像素由黑變白、由白變黑。

這不是廢話嘛!不然還得怎麼做?就這樣,我很快地放下手邊的教科書,不再去理他。

一直到前一陣子我在 Stack Overflow 這個程式設計的討論網站裡看到有些人提到,學 Functional Programming 可以學到另一種思考的方式,我這才決定重新好好學起。

而且拜這本 Real World Haskell 所賜,雖然我還只讀到第三章,但這次我終於比較知道函數式程式設計到底是什麼東西,還有 Haskell 到底有什麼特別之處。

因此接下來的這個系列,我會記錄我在學習 Haskell 時碰到的問題、解法,以及對於 Functional Programming 和 Haskell 的一些想法。

將本文加入 Hemidemi 書籤
Brian Hsu (墳墓)
2008-09-30 (週二) 19:28:23

[D 語言] SumOfSquare

此文評價一顆星二顆星三顆星四顆星五顆星
Loading ... Loading ...

這是『爪哇學校的危害』一文裡提到的問題,原本是用 Scheme / JavaScript 寫的,但很不巧的我兩個都不會,所以自己用 D 語言幹了一個同樣功能的版本。

另外,原始的問題中兩個版本的答案都沒有告訴你為什麼要這樣寫,和到底這個程式是怎麼跑的。雖然如果你熟悉遞迴的運作的話,應該花個十分鐘(是的,像我對遞迴已經很不拿少,但花點精神還是可以想出為什麼是這個答案的)就可以理解這個答案,但我還是把註解補上了。

當做展示 D 語言模版、匿名函式和 call by reference 強大之處的範例吧!

  1. import tango.io.Stdout;
  2.  
  3. // combiner  : combine function
  4. // nullValue : 達到 recursion 邊界時的返迴值
  5. // list      : 要計算的 list
  6. // (T)       : 代表要處理的資料型態
  7. T accumulate(T) (
  8.     T function (T x, T y) combiner,
  9.     T nullValue,
  10.     ref T [] list)
  11. {
  12.     // 遞迴邊際條件是如果 list 已經空了
  13.     if (list.length == 0) {
  14.         return nullValue;
  15.     }
  16.  
  17.     // 這兩行相當於 JavaScript 裡的
  18.     // list.shift()。
  19.     //
  20.     // 注意, list 一定要宣告成 ref 才可以,
  21.     // 不然會失效。
  22.     final auto first = list[0];
  23.     list = list[1 .. list.length];
  24.  
  25.     // First 沒變過,代表 First  是下一個要
  26.     // 計算的,而後面的那一串則是已經計算完
  27.     // 的。
  28.     //
  29.     // 是的,實際上我們是從陣列的最後一個開
  30.     // 始平方。
  31.     return combiner ( first,
  32.                       accumulate!(T) (
  33.                         combiner,
  34.                         nullValue, list));
  35. }
  36.  
  37. T sumOfSquare (T) (T [] list)
  38. {
  39.     // 既然如此,那麼我們的 combine
  40.     // function 自然就是把還沒計算的
  41.     // x 平方,再加上已經計算完的 y。
  42.     auto combiner = function T (T x, T y) {
  43.         T sum = x * x + y;
  44.  
  45.         // 把計算過程印出來,就會一目了然
  46.         Stdout.format (
  47.             "x:{} * x:{} + y:{} = {}",
  48.             x, x, y, sum
  49.         ).newline;
  50.  
  51.         return sum;
  52.     };
  53.  
  54.     return accumulate!(T) (combiner, 0, list);
  55. }
  56.  
  57. void main ()
  58. {
  59.     // 同樣的函式可以用在整數陣列上
  60.     auto integers = [1, 2, 3, 4, 5];
  61.  
  62.     Stdout.format (
  63.         "sumOfSquare ({}) = {}\n\n",
  64.         integers,
  65.         sumOfSquare(integers)
  66.     );
  67.  
  68.     // 浮點數陣列當然也可以
  69.     auto floats = [1.1, 2.2, 3.3, 4.4, 5.5];
  70.     Stdout.format (
  71.         "sumOfSquare ({}) = {}\n\n",
  72.         floats,
  73.         sumOfSquare(floats)
  74.     );
  75.  
  76. }
將本文加入 Hemidemi 書籤
Brian Hsu (墳墓)
2008-09-24 (週三) 17:23:54

[D 語言] GtkD 的不規則視窗。

此文評價一顆星二顆星三顆星四顆星五顆星
Loading ... Loading ...
用 GtkD 做出的不規則視窗(是我心愛的蜜爾菲優喲!)。用 GtkD 做出的不規則視窗(是我心愛的蜜爾菲優喲!)。
用 GtkD 做出的不規則視窗(是我心愛的蜜爾菲優喲!)。(View at PicasaWeb)

前幾天 GtkD 這個給 D 語言用的函式庫終於正式發佈 1.0 版了,測了一下,在 Windows 上也能很簡單的跑起來,中文也沒問題,所以就想來試一些比較好玩的東西。

剛好想到一些相當萌的桌面小幫手(例如偽春菜之類的),執行的時候,那些美美的人物自然不會被醜陋的視窗困住,那麼能不能用 GtkD 做出這個效果呢?剛開始的時候因為不知道要用哪些 API 來做,所以花了很長的時間找資料和 Try & Error,但沒想到真的拼出來後竟是意外的簡單。

另外,小小抱怨一下,GtkD 上的 API 文件實在太舊啦,害我不時得直接去翻原始碼,一整個囧啊!

以下,就是這次的成果,其實除了顯示我心愛的蜜爾菲優外,什麼事也不能做。上圖則是是在 Windows XP 上跑的情況。

而這個小實驗,應該也證明了 D 語言已經足以用來開發視窗程式囉,這下可以直接把 Java 丟一旁啦,D 語言萬歲!

最後,這個程式主要是參考這個範例修改而來的,如果你是使用原始的 GTK+ 函式庫,可以參考那個範例,一樣可以做出相同的效果。

  1. import tango.io.Stdout;
  2.  
  3. import gdk.Pixmap;
  4. import gdk.Bitmap;
  5. import gdk.Pixbuf;
  6.  
  7. import gtk.Widget;
  8. import gtk.Main;
  9. import gtk.Window;
  10. import gtk.Image;
  11.  
  12. // 我們只需要 Image 與 Bitmap mask
  13. Image loadFromFile (char [] filename,
  14.                     out Bitmap mask)
  15. {
  16.     // 因應 API 需求,我們用不到
  17.     GdkPixmap * pixmap;  
  18.  
  19.     // 建立圖型
  20.     Image image = new Image ("test.png");
  21.  
  22.     // 取得 mask
  23.     image.getPixbuf.
  24.     renderPixmapAndMask (pixmap, mask, 100);
  25.  
  26.     return image;
  27. }
  28.  
  29. void main(char[][] args)
  30. {
  31.     // 當使用者按下右鍵離開
  32.     bool buttonPressed (
  33.         GdkEventButton * event,
  34.         Widget widget)
  35.     {
  36.         Stdout.format ("Button Prssed:{}",
  37.                        event.button).newline;
  38.  
  39.         if ( event.button == 3 ) {
  40.             Main.exit (0);
  41.         }
  42.  
  43.         return true;
  44.     }
  45.  
  46.  
  47.     // 初始化 GTK
  48.     Main.init (args);
  49.  
  50.     // 主視窗,採用彈出式視窗,所以沒有標題列
  51.     Window window;
  52.     window = new Window (
  53.         GtkWindowType.POPUP
  54.     );
  55.  
  56.     window.addOnButtonRelease (
  57.         &buttonPressed
  58.     );
  59.  
  60.     // 讀取圖片檔並取得遮罩
  61.     Bitmap mask;
  62.     Image  image = loadFromFile ("test.png",
  63.                                  mask);
  64.  
  65.     window.add (image);
  66.  
  67.     // 重點是這行,把除了圖案的部份全部遮掉
  68.     window.shapeCombineMask (mask, 0, 0);
  69.  
  70.     // 顯示所有東西
  71.     window.showAll ();
  72.     Main.run ();
  73. }
將本文加入 Hemidemi 書籤
Brian Hsu (墳墓)
2008-09-22 (週一) 18:25:43

[D 語言] 第一項修練--無限迴圈。

此文評價一顆星二顆星三顆星四顆星五顆星
Loading ... Loading ...

OK,這是在這篇『學習新程式語言的十五項修練』中找到的,我找不到原文最原始的網頁,所以附上 digg 的連結,裡頭也有一些討論。

接著會試著用 D 語言來完成這幾個練習題,用的編譯器版本是 GDC v0.24,而標準函式庫則是採用 Tango v0.99.7,作業系統為 Linux 或 Windows,如果有特別的作業系統或第三方函式庫需求,我會特別標出來。

題目採用原來英文版本的,因為題目的英文都很簡單,所以直接用英文版的,應該會比較接近題目的原意。

第一個題目是無限迴圈:

Display series of numbers (1,2,3,4, 5….etc) in an infinite loop. The program should quit if someone hits a specific key (Say ESCAPE key).

看起來雖然像是非常簡單的題目,但事實上並非如此,因為很不幸的,C 語言與 D 語言還有 Tango 的標準輸入輸出函式庫,全都是 line-buffered,到目前為止,我還找不到可以直接用標準函式庫硬幹出來,而且在 Linux 與 Windows 上都可以跑的方法。

我已經在 Stack Overflow 這個網站上提問,不過目前看來情況不怎麼樂觀。

另外,這個題目看起來簡單,但我覺得其實暗藏玄機啊!你要怎麼樣在印迴圈的同時監控使用者有沒有按下 ESC 鍵呢?而且如果使用者沒按 ESC 鍵,就應該要一直在螢幕上顯示數字,不能有停下來等使用者輸入的情況。

到最後,我的解決方式如下--用一個 Thread 來當 Keyboard Handler,專門監測使用者的按鍵動作。當然,因為標準函式庫都是 line-buffered,我只好借助 ncurses 這個 Linux 下常見的函式庫,利用裡面的函式來截取使用者按鍵的動作。

當然,ncurses 是 C 的函式庫,不過既然 D 可以直接與 C 語言進行接合(interface with C),所以這不是問題,只是這麼一來這隻程式就變成只能在 Linux 上跑囉。

以下就是這次的第一項修練,其實程式滿短的,應該很容易理解是在做什麼。而在這個範例中,你可以學到:

  1. 如何使用 C 語言的第三方函式庫。
  2. 如何建立 Thread。
  1. =============================================
  2.  
  3. import tango.io.Stdout;
  4. import tango.core.Thread;
  5.  
  6.  
  7. /******************************************
  8.  * 宣告用到的 ncurses  函式原型,只要像這
  9.  * 樣宣告後,就可以直接在程式碼中用 C  函
  10.  * 式庫中的函式。
  11.  *
  12.  * 編譯時,如果用的是 dsss,就用
  13.  * dsss -llncurses 連結 ncurses  函式庫。
  14.  *****************************************/
  15.  
  16. extern(C)
  17. {
  18.     void * initscr();
  19.     int cbreak ();
  20.     int getch();
  21.     int endwin();
  22.     int noecho();
  23. }
  24.  
  25.  
  26. /******************************************
  27.  * 這個函式會用來建立一個 Thread 監視使用
  28.  * 者的鍵盤輸入。
  29.  *****************************************/
  30. void keyboardHandler ()
  31. {
  32.     initscr(); // 初始化 ncurses
  33.     cbreak();  // 讓標準輸不要做 buffer
  34.     noecho();  // 不顯示使用者按下的鍵
  35.  
  36.     // 一直重復等使用者的按鍵輸入,直到使
  37.     // 用者按下 ESC。
  38.     while (getch() != 27) {
  39.     }
  40.  
  41.     endwin();  // 結束 ncurses
  42. }
  43.  
  44. /******************************************
  45.  * 主程式,不用講了。
  46.  *****************************************/
  47. void main ()
  48. {
  49.     // 建立並執行監視鍵盤的 Thread
  50.     Thread handler = new Thread (
  51.         &keyboardHandler
  52.     );
  53.  
  54.     handler.start();
  55.  
  56.     // 無限迴圈
  57.     for (int i = 0; ; i++) {
  58.         Stdout.format ("{}\r\n", i).flush;
  59.  
  60.         // 如果建立的 Thread 已經不再執行狀
  61.         // 態,代表使用者按下了 ESC  鍵,跳
  62.         // 出無限迴圈。
  63.         if (handler.isRunning == false) {
  64.             break;
  65.         }
  66.     }
  67.  
  68.     return 0;
  69. }
將本文加入 Hemidemi 書籤
Brian Hsu (墳墓)
2008-09-19 (週五) 17:49:31

[DreamHost] 使用 TWBBS 網域。

此文評價一顆星二顆星三顆星四顆星五顆星
Loading ... Loading ...
一、先新增 twbbs.org.tw。一、先新增 twbbs.org.tw。
一、先新增 twbbs.org.tw。

這兩天陸陸續續看到有人例用搜尋引擎找 twbbs 和 DreamHost 來到這個網站,我想或許是在找如何讓 twbbs.org.tw 的免費網域名稱可以 host 在 DreamHost 上,所以就來寫一下簡單的教學吧!(先說聲抱歉藏私了這麼久,我知道現在 Google 上你是找不到讓 twbbs 網域指到 DreamHost 的教學的,想當初我也是找了好幾天都找不到,才寫信給 DreamHost 客服的啊。XD)

二、再新增你的子網域名稱。二、再新增你的子網域名稱。
二、再新增你的子網域名稱。

這篇教學,可以讓你的 XXX.twbbs.org.tw 網址連到 DreamHost 的虛擬主機,不過有一個限制,就是 XXX.twbbs.org 沒辦法使用,我不知道為什麼當我使用後者時,在第一個步驟就會失敗。不過因為我本身只使用前者,所以這個問題對我不是太嚴重,我也就沒寫信去問客服。

另外,我沒試過現在 twbbs 提供的其他網域,所以不確定其他網域是不是也是這樣做就行了,你可以自己試試看。

三、在 twbbs 設定介面中選『設定 DNS』。三、在 twbbs 設定介面中選『設定 DNS』。
三、在 twbbs 設定介面中選『設定 DNS』。

基本上,要讓 twbbs.org.tw 的網域指到 DreamHost 需要的步驟不多,不過第一步實在很難想得到,我也是寫信去問 DreamHost 的客服才知道要怎麼做的。雖然說 DreamHost Sucks,不過既然付了錢,就別客氣地寫 Support Ticket 去問客服吧。

回到正題,要讓 DreamHost 可以使用 twbbs.org 網域,需要的步驟只有以下三個:

  1. 將 twbbs.org.tw 網域利用 Fully Host 的方式加到 DreamHost 中,在這一步相關的設定資料不重要,因為用不到。
  2. 將 XXX.twbbs.org.tw 網域利用 Fully Host 加入 DreamHost,這邊就是設定你真正需要的 hosting 資料,包括帳號、PHP 版本……等。
  3. 回到 twbbs 的設定頁面,將你的 XXX.twbbs.org.tw 網址用『設定 DNS』的方式,加入三筆 NS 資料,分別指到:
    • ns1.dreamhost.com
    • ns2.dreamhost.com
    • ns3.dreamhost.com
四、如圖中填入三筆 NS 資料。四、如圖中填入三筆 NS 資料。
四、如圖中填入三筆 NS 資料。

依照上面的三個步驟做完,等大概一天左右,新的 DNS 設定生效後,你應該就可以利用 XXX.twbbs.org.tw 的網址,連到你的網頁了。如果這樣還是不行,就寫 Support Ticket 問 DreamHost 的客服吧,我上次問,還滿快就回答我怎麼做了。

另外,如果你希望像我一樣,想要讓 DreamHost 送的網域和 twbbs.org 的網域都連到同一個網站的話,只要在設定 Fully Host 的時候,選擇同樣的 FTP user 以及相同的 web directory(例如我是都指到 public_html)就可以了。

將本文加入 Hemidemi 書籤
Brian Hsu (墳墓)
2008-09-12 (週五) 17:38:35

[WordPress 外掛] Shashin 威力加強版。

此文評價一顆星二顆星三顆星四顆星五顆星
Loading ... Loading ...
都不顯示連結。都不顯示連結。
都不顯示連結。

話說經過了這幾次網站系統改版後,我就決定把部落格上用到的圖片都放在 PicasaWeb 上了,反正現在 PicasaWeb 有 1GB 的容量,我也有好幾個帳號,因此可以說是相當夠用。

比較麻煩的,就是要怎麼樣把 PicasaWeb 上的圖片整合到 WordPress 裡面,使用單純 HTML 的方式固然可行,但每次都要貼一大串語法再調到我要的版型,實在是很麻煩。

在放大時顯示連結。在放大時顯示連結。
在放大時顯示連結。(View at PicasaWeb)

在最後,我決定使用 Shashin 這個外掛,這個外掛可以很方便的嵌入 PicasaWeb 上的圖片,而且不用另外安裝其他外掛或程式碼,就有像我現在圖片上的幻燈片效果,可以說相當好用。

然而原本的 Shashin 外掛,單張圖片是沒有瀏覽工具列的,而且當使用幻燈片效果後,就沒辦法連回到原先的 PicasaWeb 網頁,對我而言有些不便。

在縮圖時顯示連結。在縮圖時顯示連結。(View at PicasaWeb)
在縮圖時顯示連結。

因此我在前幾天花了一些時間,把 Shashin 修改了一下,增加了以下的功能:

  • 每個標籤都新增了兩個可填可不填的參數--linkbackZ_YN 和 linkbackC_YN,前者會在使用幻燈片效果時,在使用者放到時於照片標題的地方顯示一個可以連回原始 PicasaWeb 相簿網頁的連結;後者則是在縮圖下方放置這個連結,連結的文字可自行設定。這個功能使用起來的結果,可以參照這頁的三張圖片,第一張是原始的版本,第二章是 linkbackZ 設成 y,第三張是 linkbackC 設成 y,第四張是兩個都設成 y。
  • 使用者可以設定是否要讓單張圖片也顯示上一張、下一張的瀏覽工具列(佈景主題必需呼叫 wp_footer() 這個函式)。
  • 可選擇在管理介面中,是否讓相簿與照片依照 Key 排序,會和在 PicasaWeb 上顯示的順序比較接近,看起來也比較順眼。
  • 選擇是否讓在 Widget 中的圖片置中對齊。
  • 可以設定幻燈片以下的特性:
    • 是否在滑鼠移出圖片後隱藏工具列。
    • 是否重覆幻燈片
縮圖、放大都顯示連結。縮圖、放大都顯示連結。(View at PicasaWeb)
縮圖、放大都顯示連結。(View at PicasaWeb)

如果想用這些功能,可以直接下載我修改過後的版本,不過因為這是我傖促間自己 hacking 出來的要用的,只是改到我自己能用,沒有很詳盡的測試,所以不保證沒有任何的問題。

另外,如果你覺得喜歡這裡面的某些功能,可以到此處討論,原開發者說會依大家的想法把這裡面的某些功能加到下一個官方版本中。

下載修改過後的版本:(.zip) (.tar.gz) (.patch)

將本文加入 Hemidemi 書籤
Brian Hsu (墳墓)
2008-09-11 (週四) 11:02:10

幸福的夜晚。

此文評價一顆星二顆星三顆星四顆星五顆星
Loading ... Loading ...

一早就跑去排隊領晚上表坊來表演的票了,第五個領到票,不過進場稍微晚了一點,還好是一個人,找位子比較容易,還是找到了不錯的位置。

簡單的講,就是一整個幸福啊!雖然都是之前看過的段子或片段,不過已經很久沒沉浸在舞台的氣氛中,加上學校的方型劇場其實不是很大,還是有點小劇場的感覺,我喜歡那種和演員貼近的氣氛。

演出的段子主要是『又一夜』的『孔子七十三賢人』、『Women 說相聲』的『戀愛病』、『亂民全講』的母語學和『千禧夜』的『雞毛黨』。

除了母語學不太熟悉外,其他都是看過的段子,七十三賢人更是早就背的滾瓜爛熟,但是現場看表演果然是另外一種氣氛,雖然因為之前已經看過,少了那麼點新鮮感,但還是不禁沉浸在那種狂熱的氣氛中。

雖然幸福,但也很遺憾啊!聽到了我最不想聽到的消息,說如夢之夢目前沒有打算要出版 DVD,那是我第二次到劇場看表坊的戲,也是第一部說什麼我都想收集到舞台劇的啊!

而且最後的特價 VCD 我也沒搶到,雖然說因為我有跑去看如夢之夢,工作人員很好心的決定要送我一套,不過沒搶到其他的 DVD 還是很可惜咧。:(

將本文加入 Hemidemi 書籤
Brian Hsu (墳墓)
2007-11-19 (週一) 22:09:54

[D 語言教學] Lesson 0: 簡介

此文評價一顆星二顆星三顆星四顆星五顆星
Loading ... Loading ...

零、前言

為什麼會有這系列的文章呢?說實話,因為發現自己的部落格快乾死了,平常的研究生生活又只是混吃等死,一個人住的日子也寫不出什麼風花雪月,所以挑一個可以湊數的主題來寫。

我很喜歡到處玩程式語言,包括 C/Java/PHP 這些很平常的程式語言,也喜歡看一些新的,不怎麼知名但卻很有趣的程式語言,像是 Pike 和 D 語言等等。

D 語言是我接觸的其中一個程式語言,也是到目前為止,我覺得用起來最好玩又順手的語言之一,也是我第一個因為自己的需求,下去寫函式庫的程式語言,由此可以看出他對我的魅力。

D 語言還很新,也有一些缺點,函式庫也還不夠多,但他確實是一個迷人的程式語言,我發現當我改用 D 語言來寫資料結構與演算法的範例時,生產力提高非常多,除錯時間也明顯變少了。

或許這個程式語言還無法拿來在正式開發場合的使用,取代 Java 或 C++,但是當個人的玩具和小工具,卻是相當優秀的一個程式語言。

一、簡介

D 語言的全名是 The D Programming Language ,是由 Walter Bright 主導開發的一個新的程式語言,他的目標是實作一個擁有 C/C++ 高效能,以及現代程式語言如 Python/Ruby 的高生產力。

舉例而言,D 語言提供了可變動長度的陣列以及關聯式陣列,你可以在程式執行期間,動態改變陣列的長度,例如以下的程式碼在 D 語言中是完全合法而且可以執行的。

  1. int [] array;       // 宣告一個還不知道長度的陣列
  2.  
  3. array.length = 3;   // 現在 array 的大小是 3
  4. array.length = 10;  // 現在 array 的大小是 10

D 語言是編譯式的語言,換句話說,你不用為了執行 D 語言的程式,去下載幾十 MB 的 Java VM 或是 .NET Framework,寫完的程式碼直接編譯成可執行檔就可以執行了。

更令人興奮的是,D 語言採用了傳統編譯程式語言的開發流程,你可以不用被龐大而難用的 IDE 搞的暈頭轉向(雖然如果你想要用 IDE 也可以),你需要做的,只是下載編譯器,然後用文字編輯器寫程式就可以了。

D 語言在很多地方,是針對 Java/C++ 的缺點而補強,也吸收了許多其他程式語言的概念,例如內建了 Contract Programming 的語法,這是 Java/C++ 所沒有的。對於 D 語言所提供的功能,以及與其他程式語言的比較,可以參考這一份文件

D 語言同時也是一個務實導向的程式語言以及系統程式語言,你可以用最高階的封裝,但是在需要的時候,你也可以用最低階的 inline assembler 以及指標來做事,但更多的時候,你不需要例用指標就可以做到指標要做的事。

舉例而言,在 C/C++ 中,要做兩個整數的交換函式,必需使用指標或參考,而在 Java 中,則必需使用 Object Wrapper,但在 D 語言中,這個函式可以寫成下面的樣子:

  1. void swap (ref int a, ref int b)
  2. {
  3.     int temp = a;
  4.     a = b;
  5.     b = temp;
  6. }
  7.  
  8. void main ()
  9. {
  10.     int x = 3;
  11.     int y = 5;
  12.  
  13.     swap (x, y);
  14. }

我們可以看見,D 語言的語法相當簡潔和易讀,捨棄了 C++ 參考的語法,直接在函式的標頭指出,a 和 b 兩個變數是以 call by refrence 的方式傳遞。

更多的例子,您可以參考這一份稀疏矩陣的程式碼,其中展示了許多 D 語言的特色,包括動態長度陣列以及 Programming by Contract 等等,同時您也可以參考這份以該程式碼直接產生的 API 說明文件

二、D 語言目前的困境

在這裡不提 D 語言的優點,因為我相信一個程式語言的優點是無法以言語傳達的,只有真的下去使用或學習,才會了解這個程式語言是不是真的適合自己。

所以在這邊,我只提出當你選擇這個程式語言的時候會遇到的一些問題,而這些問題,很可能是你放棄 D 語言的原因。

首先,D 語言是一個還在開發的程式語言,雖然在前不久前才釋出 1.0 版本,把第一版的規格確定了。但目前 D 2.0 正在開發中,而目前已知 D 2.0 無法與 D 1.0 的程式碼向後相容。

也就是說,現在用 D 1.0 寫的程式,往後可能會需要重寫成 D 2.0 的版本,這是 D 語言目前的現況之一,你必需在已經確定的 1.0 與尚在開發,很有可能會繼續改變的 2.0 之中做出決擇。

需要決擇的還不只如此,目前 D 語言的標準函式庫還在爭執,D 內附的標準函式庫是 Phobos ,但是因為開發進度緩慢,有另一套開放原始碼的 Tango 正在與之競爭。

不幸的是,兩者並不相容,你必需在其中選擇其一。更麻煩的是,許多第三方的函式庫可能只支援 Phobos 或 Tango 其中一種,所以你必需做出決擇。

在這份文件中,我們將使用 Tango 做為標準函式庫,因為有愈來愈多的函式庫使用 Tango,但這是不是正確的選擇,沒有人知道。

這是你在使用 D 語言之前必需知道的,也是 D 語言最讓人不安的部份,你不知道現在寫的程式碼,以後能不能繼續無痛的維護下去,如果選錯邊,將來可能要付出更多的心力。

這也是為何我還不推薦把 D 語言用在正式的場合,而只適合當做個人工具的最大理由。

將本文加入 Hemidemi 書籤
Brian Hsu (墳墓)
2007-10-23 (週二) 14:01:01