The Old Blog Archive (Traditional Chinese), 2004-2009

老在實驗:生活的再analog化(re-analogization)… 失敗

去年在歐洲遊玩的時候,曾經跟朋友某R講到,「應該來過再度analog化的生活」… Yes, “Return to Analog Movement”,好比說,用傳統相機。好比說,不帶手機。好比說……

好比說,不用 instant messenger ,改寄 poste restante 郵件。

說來 poste restante 是古代的東西了,世界各國郵政機構彼此有協議,只要信件標上 poste restante,寄到指定郵局,郵局就會將信件存放若干時日(通常是三個月至半年),收件人就可以憑身份證明,去該郵局領取。

Poste restante (字面意思是 kept mail)原先是設計給沒有固定收件地址的信件用的。好比說,假設你人在巴黎,一時間下遢處還不定,於是你可以請朋友將明信片寄至巴黎總局,然後你再前往領取。

我最早知道有這麼一件事是在 1997 年時讀 Let’s Go 旅遊手冊時的事。不過多年來一直沒機會實驗。終於去年心血來潮,於前往最末站倫敦前一個月,跟朋友約好「要互寄 poste restante」信件。一張寄到倫敦,一張寄到米國綺色佳。

結果,明明皇家郵務網站上寫著有這種服務的,當時也還特別為此上網查了離我借居地最近的郵局。結果等我到倫敦該小郵局時……

“No, we no longer offer poste restante service. No sir, no more.”

啊啊啊啊, and I was shouting in my head like “penz! penz!”(騙子!騙子!)。

後來我也沒再嘗試寄到台北或其他地方的郵政總局能否成功。總之,我和某朋友R口中的「百年前的”instant” messenger」實驗──訊息往返時間以「週」為單位計算──宣告失敗。

附帶一提:後來回台灣後,本來心血來潮想說來跟朋友送個電報的,結果Western Union剛好停止了這項服務……

使用WebKit製作OV選字窗的實驗

晚上利用時間重新啟動了一個去年就進行過的實驗:使用WebKit來製作OpenVanilla的選字窗。翻譯成白話就是:「用web browser來做為輸入法的選字界面」。

由於OpenVanilla從0.7.0開始,選字窗的顯示是由另外一個process負責,也就是所謂的OVDisplayServer。Display server是透過Objective-C RPC來聯繫,因此我們只要kill掉display server的process,換上有相同RPC註冊名的process,就可以達到替換UI的功能。

目前這個實驗真的還在無比alpha的階段,以下步驟僅供the truly adventurous使用:

  1. 從svn取得OV最新原始碼
  2. 使用Xcode編製在trunk/Experiment/WebKitServer的project file (WebKitServer.xcodeproj)
  3. 在shell底下執行 “killall OVDisplayServer”,把原先的 OVDisplayServer process 殺掉
  4. 執行編好的WebKitServer.app
  5. 開啟新的應用程式(此時最好關閉舊的應用程式,不然切至OV時會發生問題)
  6. 切至OV,就會看到使用WebKit的選字窗
  7. 如果不喜歡default style,可以到 ~/Sites/ 建立一個名叫 WebKitServer 的目錄,然後在裡面建立一個 style.css
  8. 換上自己喜歡的body style

注意事項及現有問題:

  • style.css更新後,WebKitServer 不會即刻更新,得重新啟動(如何refresh WebKit中的stylesheet,得要研究一下)
  • 重新啟動 WebKitServer 代表先前啟動的應用程式也得關掉重新啟動,不然會當掉
  • 不能調整選字窗的大小和位置,不然OV會在選字窗內啟動,然後就陷入永劫回歸,orz
  • 選字窗目前是用default window style,這個得調
  • 選字窗內文目前是用重新載入HTML page source的方式做的,這個將來應該要改成用JavaScript來做:反正WebKit很邪惡嘛……

不能免俗,有圖有真相:

OpenVanilla DisplayServer on WebKit

聯考作文:A House Is Not a Home

曾經當過家教老師,也曾經對這一代高中小朋友們要經歷的考試制度感到不可思議。東吳的曾泰元老師在《中國時報》上寫了篇文章,抱怨大學指定科目考試(其實就是ex-聯考啦)的閱卷苦難,這讓我想到了我那一年(咳,「十幾年前」)的聯考英文作文題目,叫”A House Is Not a Home”。細細想來還蠻有深意的,可能是希望學生寫出什麼「沒有溫暖的房子不足以成為一個家庭」一類的高調吧。

我早就忘了自己到底胡謅了什麼空話,但我卻對離開試場後說的「其實可以這樣寫」印象非常深刻。我跟我的同學們說:

「其實這題目非常好寫啊!你想想看,”A house is of course not a home. Why is that? First of all, as you can see, there is Penthouse, but never Penthome (or full house but never full home, for that matter). In addition, you can hit a home run, but never a house run. Their differences are huge! Based on those observations, therefore, we can come to the conclusion that a house is of course not a home. Nothing is more evident than that. Quod erat demonstrandum. Fin.”」

註:上述標粗體的,都是高中英文老師說寫作文時要用的連接詞…… (冷)

後來自己當過家教老師,我教過高中生寫作文、做閱讀,也教過大學生準備考台灣研究所的英文考試(主要還是作文)。我跟家長約法三章:我不管學校教了什麼、我不在乎現在的考試制度怎麼變怎麼考、我只教我想教的東西。家教學生多半有一些共通性(不然就會去補習班了),而我所看到的共通性則還包括了一個東西,叫做對生活的沒興趣。沒興趣去瞭解自己眼界以外的事物,以及沒興趣說故事,就造成了這些小朋友閱讀和寫作上的障礙。

所以後來我採取的策略很簡單,小朋友哈日,我就去找跟日本音樂有關的評論文章(那時Time出過幾篇對J-Pop,e.g. 小室哲哉的評論或是Keiko的bio──um-hum, right, that was some time ago…)。文章很難沒關係,因為單字從來都不是(一般性)閱讀的問題。我們的英文教育不善教人從上下文學到新東西,而注重「預先學過的單字」這種被動式的知識,結果就是容易造成閱讀的挫折。事實上,我教小朋友們,閱讀測驗只是個problem domain固定的符號遊戲(當然我沒說得這麼故作艱深),只要稍微會一點頻率和語境分析,不難猜出句子之間的邏輯關係,以及找出選擇題要問的答案在那裡。

至於寫作,我完全放棄了教他們用英文表達的念頭。最後乾脆走巧門,叫小朋友們把平時的中文作文拿來,先把他們的中文作文劈頭砍一遍,然後叫他們把英文作文當翻譯題(同樣是減少問題複雜度),每個句子都只用現在式、第一人稱、SVO,前頭或中間再插入最標準、簡單的連接詞,十二句話搞定一篇考試用文章交差,voilà。(教大學生的話層次當然就不同了,不過原理是一樣的。)

那段日子靠家教度過了休學期間的一段青黃不接。小朋友們後來一一報來好消息,像是閱讀測驗拿了七八成分數,作文也拿了個基礎分等等(單字、文法還是一塌胡塗,但我之前就已經跟她/他們家長說我只教閱讀寫作,不包生的)。絕對不是那種補習班名師所能誇口的表現。在國外唸理科的同學倒是這樣安慰我:「你想想,她們之前模擬考作文都是0分,而你讓她們指定科考能考出分數來,那可就是百分之無限大的進步了」。

註:稱呼高中生「小朋友們」沒別的意思,請ignore我這種三八當可愛的diminutive使用方式(除非,你的意思是說,我這種用法表現出我有某幾種控的傾向,那我也只能說,什麼什麼之罪,什麼什麼無詞的了,orz)。

OpenVanilla 2006年6月捐款收入及支出帳目總覽

2006年6月份的OV捐款收入及支出帳目總覽,報表已經編制完成,請點選這裡下載或瀏覽報表。報表是文字格式、UTF-8編碼,並帶有lukhnos的gpg簽名。

公共衛生、演講技巧、資料的共享

沒什麼好多說的,看了就知道。繼Identity 2.0之後又一次演講與表現技巧大衝擊。And more profound (I was blown away).

輸入法開發實驗:地理資訊輸入法(三),”Hello, world!”

在先前兩篇準備工作,我們做了幾件事:需求分析、可行性分析、原始資料的分析與準備,並且建立了一個計劃所需的「周邊」(periphery)。我們這次要開始動手了。

Introducing… OpenVanilla

我們終於可以開始一展 OV 發展的初衷:簡化輸入法的開發工作。這一篇blog會比前兩篇都短得多。我希望我們利用這一篇做為 OV 的某種 crash course ,讓大家馬上就編出一套簡單的輸入法──換個方式說,先做出一套「輸入法的 “Hello, world!”」,再慢慢細述一個輸入法有哪些構成。

所以今天的內容會相當簡單。I promise.

輸入法的 “Hello, world!”

什麼是輸入法的 “Hello, world!” 呢?最簡單最簡單的輸入法,其實就是:鍵盤打了什麼,就直接輸出什麼。用網路的術語,這叫 “echo back”,或者叫回送。把使用者打的鍵、也就是餵進輸入法的鍵碼,再原封不動地送回去。事實上,在輸入法上,這也可以稱為 “pass thru”,也就是不做任何處理。

不過,在以下的範例中,我們還是會在這 pass thru 的過程中,再多動一點手腳。

以下就是今天的程式範例,我稱為 FoobarIM.cpp (「foobar 輸入法」):

#include <stdio.h>
#include <Openvanilla/OpenVanilla.h>
#include <Openvanilla/OVLibrary.h>

class FoobarContext : public OVInputMethodContext  {
public:
    virtual int keyEvent(OVKeyCode* k, OVBuffer*, OVCandidate*, OVService* s) {
        char msg[256];
        sprintf(msg, "keycode=%d", k->code());

        s->notify(msg);
        return 0;
    }
};

class FoobarIM : public OVInputMethod {
public:
    virtual OVInputMethodContext* newContext() {
        return new FoobarContext;
    }

    virtual const char *identifier() {
        return "FoobarIM";
    }
};

OV_SINGLE_MODULE_WRAPPER(FoobarIM);

請將以上的程式碼範例,存檔成 FoobarIM.cpp 。然後,進到 shell ,利用下面這行指令編譯:

g++ -bundle -o FoobarIM.dylib FoobarIM.cpp

編完之後,我們就會得到一個 FoobarIM.dylib 。再將完成品用下面的指令,拷到自己的 OV 模組目錄裡:

cp FoobarIM.dylib ~/Library/OpenVanilla/0.7.2/Modules/

好了,大功告成。請隨便新開一個應用程式(例如 TextEdit.app)──請注意,要是新開啟的應用程式才行──然後,拉下你的 OV 選單,你應該會看到如下例的圖片:

FoobarIM in action

FoobarIM 輸入法正式進入選單中!

你可以選用這個輸入法,在 TextEdit.app 中隨便打幾個字,看看會有什麼東西。

So, 這就是最簡單的 OV 輸入法了。我們會在下一篇中慢慢解釋,上面看似怪異的程式碼,到底做了哪些事情。

使用 svn 取出範例

事實上,我們先前建立版本管理倉庫,有一個好處是:我們完全可以用版號來分辨目前進行到哪一個階段。例如,如果你想知道今天之前我們放進了哪些東西,你可以用 svn co -r 2 http://svn.openfoundry.org/ovgeoinfo 這個指令,來取得「revision 2」的所有程式碼。

今天送進去 svn repo 裡的東西為 revision 3。我們之後還會繼續送新的東西進去。用 svn 的好處已經很明顯:我們不再需要開啟例如 step1/ step2/ step3/ … 等等目錄。我們直接用 svn 的 co 指令,就可以取得某一個特定版號的所有程式碼。

輸入法,諸行無常

原來,是因為llwang發了一張ticket:OpenVanilla的日文符號輸入法,能不能加上長音符號(例如指揮家ディレクター的那個「ー」)、同字符號(例如《威風堂々行進曲》的那個「々」)等等常用的符號?這件事我一直拖了很久,即便是0.7.2推出了都還沒完成。

然後差不多是昨天吧,突然因為很想回x5問我的問題:「为什么你要在个别的中文词汇后面附上英文翻译?感觉就像这篇中文日志是你从英文翻译过来的!」。這個問題我一直都有在想,所以本來我也打算搬出了我想過很久的答案:「因為中文沒有日文的訓讀(kun-yomi),沒有辦法表現出一個詞的表面義與作者借用此漢字詞所表現的意欲義(intended meaning)間的歧異性……」

嗯,不過,那種「在漢字上面加註假名」的東西,說來並不叫訓讀。

訓讀(訓読み)是日文的特有現象,意思是拿和語(日語的本體)的音來讀漢字。例如「山」的音讀是源自漢語的san,但是訓讀為yama(例如山葉=yamaha)。訓讀涉及的讀音與字詞型態,然而在書寫文字上加註假名的這一件事,則應該被稱為振假名(振り仮名)。而,那些寫在漢字上的小字,也有個專門術語,叫ruby text。

振假名算是日語書寫系統一種得天獨厚的設計。巧妙的振假名使用,可以表達詞語的雙義性(double-entendre):例如明明假名寫的是一個意思、其實作者想表達的是漢字的意思。或者其實作者想表達的是用假名寫出的意思(例如外來語),但為了說明其義,我們標上漢字。當然,在口語上,人們唸的會是假名標註的音。例如,漫畫中,美軍飛行員收到指令後,講了一句「瞭解」,而瞭解旁邊注上了「ラジャー」(roger)的假名,這意思是說,這位飛行員講的其實是英文(在日文裡,外國人即使說日文,也都是用片假名標示的),但是為了解釋「roger」是什麼,漢字寫上了「瞭解」兩字。振假名不只是一種工具或漫畫用來玩文字遊戲的手段,這種設計讓文字可以輕易背叛自己,或者揭露了漢字其實依靠詮釋來支撐的本質。

「假名」(kana)一詞隱含了「真名」(mana)的存在,而確實在日文裡「真名」指的是漢字。但是,如果說假名是哪個日本留學中國的僧人在一時一地所發明的,那就要貽笑大方了。

就在這求證「到底那個在漢字上標註假名的書寫行為」的過程中,我掃到了一個熟悉的關鍵詞。

伊呂波歌。

Iroha.

OpenVanilla日文符號輸入法最早的名字。

拿中文輸入法輸入日文詞,是個遠自古老時代(倚天中文系統)以來就有的需要。那個時候,我們有不完整的japan.box。到了Windows時代,我們有「櫻花輸入法」。而OpenVanilla計劃開始沒多久,我們有了第一個由使用者創建的輸入法表格:momizi從japan.box衍生而來的「Iroha」。

OpenVanilla的「Iroha」表格,除了可以輸入拗音(例如きょ、みょ)這一類先前japan.box付之闕如的特殊功能外,momizi也在表格中增加了 “iroha” 一詞,只要輸入 iroha ,就會出現「伊呂波」三個字。照momizi的說法

打 iroha 出「伊呂波」,以向集成日文假名、定下日語假名基礎的《伊呂波歌》(最古的版本成於西元 1079 年)表示致敬。

按照百科全書的說法,雖然一般認為《伊呂波歌》為學問僧空海(Kūkai, 774-835)所做,但從語音演變的歷史來看,其實是不太可能的。

《伊呂波歌》等於確立了片假名在日文中的地位,該詩除了「ん」(n)外,現行的日文平假名的每一個字,都在這首詩中出現了一次(「ゐ」與「ゑ」於現代日語中不再使用):

色は匂えど (いろはにほへと)
散りぬるを (ちりぬるを)
我が世誰ぞ (わかよたれそ)
常ならむ (つねならむ)
有為の奥山 (うゐのおくやま)
今日越えて (けふこえて)
浅き夢見し (あさきゆめみし)
酔ひもせず (ゑひもせす)

詩歌便是因為首行的頭三個字「いろは」(いろ是顏色及佛教中「色」的意思)而得名。如果寫成羅馬拼音,便成為:

iro wa nioedo
chirinuru o
wa ga yo tare zo
tsune naran
ui no okuyama
kyou koete
asaki yume miji
ei mo sezu

根據《大辭泉》的說法:「いろは」可以指「假名」,也可以指「初學的入門事物」,就這一點上和英文的ABC是一樣的。事實上,在我們今日熟知的「五十音順」出現前,日文假名的排列,其實是依循著上述的「伊呂波順」排列。也就是說,曾經在很長一段的歷史長河裡,只要學會了《伊呂波歌》,就學會了日文的假名。

然而,最令人想不到的是,《伊呂波歌》的意涵,是這樣的:

花開溢香,然而不日也會凋謝;
我世之人生又豈有終此不變?
如今將超越此生,渡無常之深山,
速從淺夢中醒來,莫沉迷

而這樣的詩句,據稱是一解《涅槃經》中此偈

諸行無常
是生滅法
生滅滅已
寂滅為樂

沒想到,原來只是做為輸入法表格的名字(iroha.cin),竟然有這麼深的意義。

OpenVanilla到了0.7.2的時候,為了包裝的方便,在zonble和momizi的同意下,我把「漁村符號」(也就是著名的♨♨♨輸入法)與「Iroha日文假名」兩個輸入法表格,合併為了ehq-symbols.cin,也將輸入法的英文名改為了「Symbols and Kanas」。

看完這一系列的文章,我想,還是應該把英文名改為「Symbols and Kanas (Iroha)」。

同時也真的該來把llwang的ticket給完成了……。

後記

今天上午,我把這張ticket給關閉。最新版本的ehq-symbols.cin加上了llwang所提出的各種符號,同時也將英文名(%ename)加上了iroha字樣。最新版本的檔案可從OV的svn repo中取得

又,中文使用者有輸入日文的需要。那麼日文的使用者會不會也有輸入中文的需要呢?看來的確是有的。這裡就有這樣一個例子。

《伊呂波歌》的中譯等資料感謝momizi的提供。在此也以本文向《伊呂波歌》致敬。

輸入法開發實驗:地理資訊輸入法(二),更多的準備工作

上一篇文章裡提到,我們想開發一個「(以任何現有輸入法)打完地名,輸出與地名相關之地理資訊」的新「輸入法」──或者是「超輸入法」(meta input method)。我們在上一篇文章針對了需求做過了分析,並評估了一下OpenVanilla能派上什麼用場。我們拿到了一份「生」的XML的原始資料,將它煮成了SQLite資料庫所需的SQL指令檔。

我們針對功能核心(functional core)的準備工作差不多到此。在今天這一篇,我打算從另一個方向來進行準備。這些事項通常被認為只是一個計劃的「邊緣」(periphery),或者用不那麼哲學的說法,叫做「支援/支持性結構」(supportive structure)。然而我們可以從任何一個初具規模的open source計劃中發現,這些「邊緣」與「支援/支持性結構」的重要性,絕不亞於功能核心。

好的,那麼今天的準備工作,將包括有:

  • 在OpenFoundry上開設一個計劃
  • 利用OpenFoundry所提供的原始碼倉庫(source repository),將我們目前為止準備的材料送進去(commit)。
  • 為我們的計劃選一套open source的授權條款。

準備好了?我們開始。

為程式碼找一個家

剛開始的時候,一切都很單純:你把程式碼編好,把東西包裝起來,上傳到自己的homepage所在地,修改index.html(或其他網頁檔案),然後昭告親友,你寫了一套新軟體。

然而軟體會長大,計劃也會成長。你可能開始想找人參與,或是有人主動想開發新的功能。你開始有共同工作(collaboration)的需要。另一方面,你也可能開始有各種不同的版本要發布。你想要使用subversion一類的工具來管理程式碼,可是又不想或無法自己架subversion在server端所需的各種工具。然後你還需要有bug tracker、mailing list,你發現你開始分心去做這些「務虛」但必要的管理工作……

許多龐大的open source計劃,因為資源較為足夠,得以發展出一套完整的分工體系,讓其中一部份參與者擔負各種管理及「顧家」(housekeeping)的工作。這些大型計劃像是自給自足或自成體系的大企業。然而我們只是個剛開始的計劃,我們不可能花費這些心神來處理這些「顧家」的工作。

像SourceForge、OpenFoundry一類的服務,所幫你分擔的,就是這種「安家」(hosting)的憂愁。它們為你的計劃提供一個家、一個發布的空間、一套具有版本管理功能的原始碼倉庫,並提供一些基本的計劃管理工具,供你的計劃公開給世人使用。

可以說SourceForge、OpenFoundry是一種公共服務,我們可以把各種顧家、安家的工作「外包」出去,利用它們來節省我們的管理時間。

到OpenFoundry上開一個計劃

在這個時點上選擇OpenFoundry的理由,不外是它有中文介面,又不像SourceForge那麼複雜。

要在OpenFoundry上開一個新計劃的方法很簡單。首先,你要先成為註冊使用者,然後就可以開設新計劃。

我們在計劃資料上填上以下的內容:

  • 計劃名稱:地理資訊輸入法
  • 計劃的id:ovgeoinfo (我們將來以此名稱來存取原始碼倉庫)
  • 版本管理工作:使用預設的 subversion

然後,在選擇授權的時候,我們使用 BSD License ,文件的授權則使用Creative Commons的Attribution-NonCommercial-NoDerivs。

稍後我會說明(在此時點)做這個選擇的理由。

將原始碼送進原始碼倉庫中

嗯,到此為止,我已經用了兩個我實在用不慣的中文詞,幾乎快到爆炸的程度。以後我會只講check in、check out跟source repository(簡稱repo)。

在OpenFoundry上把計劃開設好後,我們就可以透過這個URL來存取和計劃相關的資訊。同時,OpenFoundry也會幫我們設定好計劃要用的subversion repo:http://svn.openfoundry.org/ovgeoinfo

接著,我們利用subversion這套工具,把先前寫的Perl煮XML工具,以及範例的資料檔,check in(或說commit)進入我們的repo中。

使用subversion

Subversion屬於UNIX「一脈相承」的的版本管理系統,它的使用方式和命令語法都承襲自CVS,而CVS又是承襲自RCS。目前Subversion的中文文件,最重要的,當然要屬plasmaball翻譯的手冊全文。不過,subversion雖然功能強大複雜,對於我們來說,日常的工作,其實只有以下四種:

  1. 用svn checkout指令取得原始碼
  2. 用svn update指令將放在我們自家硬碟裡的拷貝,更新到最新版本
  3. 用svn commit指令將我們做過的修改,送進repo裡
  4. 用svn log瞭解最近的更動記錄
  5. 萬一在svn update時遇到衝突,做衝突的修正

其實 (1.) 只有第一次工作時會需要用到。一般對只需要取得程式碼、或僅有唯讀(read-only)權限的人,也只會用到 (2.) 和 (3.),只有真正在修改程式的人會需要碰到 (4.) 和 (5.) ,而且 (5.) 的情況不多見。

將repo目錄checkout到自己硬碟上

Ok, 我們可以把repo給checkout出來了:

svn co http://svn.openfoundry.org/ovgeoinfo

事實上,當你看到這篇文章的時候,我已經把上一篇文章提到的幾份東西,都一一送了進去,除此之外還有很多別的東西。為了說明方便,我想先「還原現場」,向你說明我在第一次checkin時,到底送進去了哪些東西。

我送進去了哪些東西

如果你觀察一下 checkout 出的 ovgeoinfo/ 目錄,你會發現如下的結構:

ovgeoinfo/
  trunk/
    License/
    Mk/
    Module/
        OVGeoInfo/
    SharedHeaders/

之所以會有這樣的安排有有原因的。

許多subversion的使用者承襲了CVS的習慣,把repo最頂層區分為以下兩種目錄:

  • trunk/: 字面意思是「樹幹」,意思是程式碼的主支幹,也就是現行的最新版本(latest version)
  • branches/: 意思是「支幹」,也就是程式碼的分支。

事實上,許多計劃承繼自CVS的習慣,還有一個名叫 tags/ 的目錄,用來標示各種已經推出的版本。但是由於 subversion 不再依靠這種方式,有一些計劃(例如 OpenVanilla)把各固定的版本放在 releases/ 目錄中。

這些習慣雖然在計劃初期看不出好處,儼然只是增加麻煩,但是隨著計劃成長,這些過去open source計劃所慢慢累積出來的經驗法則,就會顯露出其效用。

放在 trunk/ 裡的東西

至於,trunk/ 裡的四個目錄,功能是這樣的:

  • License/: 這是屬於服務性質的目錄,主要將放進我們的 BSD License 全文。
  • Mk/: 這是 OpenVanilla 模組共用的 Makefile
  • Module/: OpenVanilla 的模組會放在這個目錄裡,裡面的 OVGeoInfo/ 是我們真正要開發的模組。
  • SharedHeaders/: OpenVanilla 的一些供模組使用的程式庫標頭檔。我們在此將只用到 OVSQLite3.h 這個 sqlite3 程式庫的包裝紙(wrapper)。

OpenVanilla 之所以會這樣安排目錄結構,當然也是有原因的,我們會慢慢解釋。至於為何目錄名一反 UNIX 的慣例,首字都是以大寫開頭?那是因為 OpenVanilla 受到了 Mac OS X 的目錄命名習慣影響所致。

我們可以從命名看出一個計劃的文化背景。:)

然後,我們把上一篇文章所提到的內容,都放到了 OVGeoInfo/ 這個目錄中了。

選擇 BSD 授權

大抵說來 free/open source 軟體的授權只有兩種:GPL 系的跟 BSD 系的。我選擇所謂的「三條文式BSD授權條款」有以下幾個理由:

  • 它很短
  • 比較起來算是好懂
  • 它的限制少
  • 我翻譯了一個中文版本

當然,這兩種軟體授權代表了兩種哲學差異,在此無法一一細述。

我們 reuse 了一下 OpenVanilla 的 trunk/License/ 目錄裡的授權條款文字,將之放進了地理資訊輸入法的 trunk/License 中

到此…

到此,我們完成了這些「邊緣」或「支持性結構」的準備工作了。我們可以試一下先前提過的煮XML的程式碼:


cd ovgeoinfo/trunk/Modules
./gendb.pl example.xml

你也可以用svn log指令閱讀最近的修訂記錄,了解這個實驗的最新進度。

輸入法開發實驗:地理資訊輸入法(一)

(註:其實這應該要畫很多圖來說明的,在此先略)

ilya先前和我討論到:有沒有可能拿OpenVanilla來做地理資訊的輸入工具?他的需求,簡單說來,有以下兩類:

  • 輸入中文地名,出一地的英文地名或古地名
  • 輸入中文地名,出一地的經緯度或其他座標資料

在和ilya以及中研院OpenGIS Lab的張欽隆討論後,我們決定將這個輸入法的開發,變成一種公開的實驗。

這篇文章便是這一系列實驗的第一篇。我的初步構想是:藉這個機會,來實現OpenVanilla最初設計的其中一個理想──簡化輸入法的開發過程,讓開發者能專心於輸入法的邏輯和演算法,而不需煩心於各種作業系統平台的實作細節。

同時,我也希望藉由這個實驗,來說明OpenVanilla晚近加入的一個新設計──所謂的「詞彙管理架構」,以及為什麼詞彙管理架構能夠填補現有輸入法設計的不足。

最後,也希望藉由這個實驗,來走一遍一個 open source project 的開發流程,包括了建立程式碼的版本管理倉庫、選擇版本授權等等雖然屬於「周邊」(peripheral)、但對於計劃延續不可或缺的部份。

問題分析

地理資訊的輸入是一件麻煩的事。由於與一個地名相關的資訊種類繁多,如果能以「地名」為中心,也就是拿地名來當key,便能輸入各種資料,對於地理相關工作應該是很有幫助的。Ilya的需求是由此出發的。

同時,地名也是一種有歷史性(historicity)的東西。地名不只是現時、此刻的存在,也代表了歷史的累積。打當今地名而能出古地名的需求,也是由此而衍生出來的。

輸入法所可能提供的協助

輸入法在軟體工程上是一種奇特的存在。雖然輸入法最早是輸入亞洲語文所不可或缺的工具(sine qua non)[1],但是本質上輸入法是一種變換(transform):輸入一串組字鍵碼,得出組字結果。嚴格說來輸入法其實就是一種索引鍵─鍵值(key-value)的查找關係。

而,因為輸入法在現代桌面環境上,可以與任何需要文字輸入的GUI元件共同工作,於是很奇異的,輸入法變成一種增加軟體功能的方法。也就是說,藉由擴充輸入法的功能,我們等於同時也擴充了應用程式的功能(簡繁轉換及標點符號轉換均為其例)。因此,輸入法可以被當成一種「非侵入式的外掛」(non-intrusive plug-in)。

然而,由於主流的輸入法架構,僅僅作到一次性的key-value查找(例如:以倉頡組字碼找中文字),因此就算我們打出了「台北」兩字,我們依然只能把這兩個字剪貼到另一套資料庫程式,藉以檢索與「台北」兩字相關的地理資訊。

OpenVanilla早期設計了所謂的「輸出過濾器」(output filter)的架構,是可以針對「以詞為輸出單元的輸入法」(例如酷音,每一次輸入都是一個詞)做輸出處理。例如,我們可以設計一個「打『台北』出Taipei」filter。然而過濾器的缺點是:在UI邏輯上,filter是一種單向的、多對一的(one-direction, many-to-one)轉換。對於地理資訊來說,我們需要一個反饋的過程,亦即,當打完「台北」之後,我們可能要讓用戶再一次選擇與「台北」相關的地理資訊。

這種類似「二次選字」的事情,用filter是無法做到的。

OpenVanilla的詞彙管理架構

OpenVanilla於2006年初提出了「詞彙管理架構」。事實上一套「詞彙管理架構」是由兩個元件組成的:置如輸入法之前的「鍵碼前置處理器」(Key Preprocessor)跟「詞彙補捉器」(Phrase Catcher)。詞彙補捉器其實是一個filter,只是不停地把使用者輸入的文字給收集進來。

這樣的好處是,讓非以詞彙為輸出單位的輸入法(例如倉頡,一次只輸出一個字),也能夠收集詞彙。而,透過置於輸入法之前的「鍵碼前置處理器」,我們可以在還沒進到輸入法之前,就先對使用者的按鍵做處理:例如,把從「詞彙補捉器」收集來的詞,丟進資料庫裡,或是拿來查找。

也就是說,透過 key preprocessor ,我們可以做到一個「超輸入法」(meta-input method),讓使用者可以用任何一種輸入法打完「台北」兩個字,然後再拿「台北」兩字做一些事。

由於 key processor + phrase catcher 這樣的架構可以做到「再一次的查找與轉換」,我們的地理資訊輸入需求,似乎可以再利用(reuse)這個 pattern 來達成我們的需求。

實作的方法

通常,在原型階段,我們會先用一些 dirty hack 以及 top-down approach ,很快地把程式的功能原型寫出來,來證明概念可行。在此之後,我們再改走 bottom-up approach ,細細烹煮(cook)每個所需的元件,最後再放到一起大火快炒,勾芡上桌。

由於設計樣式已經有了,這個設計也被證明可行,我們要做的是依照需求,重寫一套依地名查找地理資料的輸入法。

在此之前,我們得先把一些材料準備好。

材料:地理資訊的 XML 資料表

我們收到的是一份長相如下的 XML 資料表:

<place id='1'>
    <zh_name>漁人</zh_name>
    <en_name>Yuren</en_name>
    <lon>121˚ 32' 40"</lon>
    <lat>22˚ 1' 5"</lat>
</place>

以上是該 XML 資料表的簡化版,我們先列出中文地名、英文地名與經緯度四項資料。

生的XML資料並不是那麼好利用。OpenVanilla推薦使用SQLite3。因此我們得先把XML煮成SQLite3能用的資料。

最懶的方法:XML::Simple

煮 XML 文件的方法很多,最懶又最快的方法,當然是使用 Perl 的 XML::Simple 模組了。

#!/usr/bin/perl -w
use strict;
use XML::Simple;
my $filename=shift or die "Must specify a filename";
my $data=XMLin($filename);

嗯,好像這樣就煮完了,似乎沒什麼挑戰性。當然,如果你只裝 XML::Simple ,在 OS X 上可能會發生一些問題(XML::Simple 預設的 XML parser 是 XML::SAX::PurePerl ,在 UTF-8 判斷上似乎有點 suck),因此建議再安裝 XML::SAX::Expat ,就會順心如意多了:

sudo cpan install XML::SAX::Expat
sudo cpan install XML::Simple

然後是怎麼轉換到 SQLite3 的問題。比較笨的作法是用 Perl 的 DBI 模組,然後實際開啟一個 SQLite3 資料庫,把轉換好的 $data 依欄位一個一個寫進去。

但是,這樣做,就實在太不懶惰了。SQLite3 的好處是,我們完全可以把資料生成另一堆生的 SQL 指令,然後讓 SQLite3 來吃。因為對 SQLite3 來說,一個資料庫就是一個檔案,所以萬一不高興,反正砍掉重練就是了。

所以,我們把煮成了 Perl hash 的資料一個個倒出來:

my $places=$data->{'place'};

for my $x (sort(keys(%$places))) {
    my $d = $places->{$x};

    print "INSERT INTO geoinfo (zh_name, en_name, lon, lat) VALUES (",
        sqlquote($d->{zh_name}), ", ",
        sqlquote($d->{en_name}), ", ",
        sqlquote($d->{lon}), ", ",
        sqlquote($d->{lat}), ");\\n";
}

也就是說呢,我們把資料 dump 成一條又一條的 SQL INSERT 指令。

那個 sqlquote 是一個小的函數,幫忙我們把字串加上 SQL 的 quote:

sub sqlquote {
    my $x=shift;
    $x =~ s/\\'/\\'\\'/g;
    return "'$x'";
}

然後,我們在倒資料前,先加上建立資料庫 schema 和 index 的指令:

print "CREATE TABLE geoinfo (zh_name, en_name, lon, lat);\\n";
print "CREATE INDEX idx_zh_name ON geoinfo (zh_name);\\n";
print "BEGIN;\\n";

並在最後加上

print "COMMIT;\\n";

之所以要用 SQL transaction 來做插入,是因為 SQLite 在 non-transaction insert 的表現慢得嚇死人。用了 BEGIN…COMMIT 之後大體上會瞬間完成所有 insert 的動作。

好了,這樣差不多了,只要再做點水管工(plumbing),我們就可以把 XML 檔案轉成 SQLite3 的 DB 了:

./gendb.pl source.xml | sqlite3 geoinfo.db

從這裡開始……

我們還沒談到去哪找地方放這些程式的問題,因此目前還不會有可以下載的地方。有了資料庫,我們就可以開始想想怎麼設計輸入法了。希望我很快就有時間來寫下一篇。

註解

  1. T. C. Chiang, D. Liu, K. M. Liu, W. Z. Yang, P. T. Tan, M. J. Hsieh, T. H. Chang, W. L. Hsu, “OpenVanilla – A Non-Intrusive Plug-In Framework of Text Services”. 2005. (Abstract).

It’s there…. OpenVanilla 0.7.2發布

這應該是 OpenVanilla 誕生到現在,在包裝上最花時間的一次。It’s finally here!

詳細的發布說明、取得方式及安裝說明──這一版請大家至少一定要讀一下「Ok,我到底該安裝哪套variant?」一節──請參考OpenVanilla 0.7.2的版本發布說明

對了,這一次 0.7.2 正式版的推出,有一個要特別感謝的人:davidyu。因為他的耐心與鍥而不捨──或許也因為他的PowerBook特別搞怪(?)──我們終於得以克服0.7.2-beta以來一直很難再現(reproduce)的一個嚴重bug。雖然這一半是OS X的問題,但至少以這次的修正來說,以後應該不會再發生「需要靠砍神秘的 .plist 才能修復 OV」的奇怪情事了。再次謝謝他的熱心幫忙。

« Previous PageNext Page »