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

Archive for July, 2007

用Ruby處理台灣語文:Formosa

在Ruby Forge上的Formosalib-formosa的延伸計劃。我們希望能使用Ruby,讓處理台灣語文更為容易,Formosa就是這樣一套gem library。

目前Formosa跟lib-formosa一樣,僅處理了台灣閩南語(Holo; Southern Min)的音節。未來希望能擴展到客語(Hakka)及其他台灣語言。

安裝Formosa的方法很簡單(前面依權限不同,可能需要加上 sudo):

gem install formosa

然後就可以在Ruby程式中使用了:

$KCODE = "u"
require "rubygems"
require "active_support"
require "formosa"
include Formosa::Holo
poj = SyllableType::POJ
tl = SyllableType::TL

# 將ASCII形式的POJ的音節,轉換成TL,下例輸入 goa2,輸出 guá
SyllableUtility.compose_syllable(poj, tl, "goa2")

下例將Wikipedia閩南語版的頭一句話,轉成ASCII形式:

SyllableUtility.convert_text_into_query_form(0, "Hoan-gêng lâi Wikipedia ê Holopedia hōng-tê!")

輸出即為 “Hoan-geng5 lai5 Wikipedia e5 Holopedia hong7-te5!”

如果把上述query form再倒進去compose_syllable中,就可以得到,例如說,台羅版的歡迎詞 “Huan-gîng lâi Wikipedia ê Holopedia hōng-tê!”:

SyllableUtility.compose_syllable(poj, tl, "Hoan-geng5 lai5 Wikipedia e5 Holopedia hong7-te5!")

formosa這個gem裡面事實上包含了一份C++版本的lib-formosa,以native extension形式存在。之所以這樣設計,主要是考量目前對於ASCII form to composed form(或者是正規化後的normalized/canonical form),還是以C++版本寫得最為完整。

不過,Ruby版本的formosa,還能夠將composed form轉換為ASCII form (query form),對於已經寫上標音號的文本(例如紅皮聖經、Wikipedia,或上述例子)的再處理上,應該有相當大的幫助。

名詞解釋

上述用了一些我們在發展OVIMPOJ(OpenVanilla的POJ輸入法)以及lib-formosa時所創的名詞,也許並非台語文資訊處理的正式用法。如果有疑義的地方還請大家指正:

  • Query form (ASCII form): 未加標音號、結尾為數字、以ASCII形式表現的台語文音節。例如goa2 (我), tai5-uan5 (台灣) 等等。Query form也是用在輸入法資料表格的形式,因此稱為query form。由於query form只允許將調號放在音節後,因此也同時隱含了音節恆為正規化形式(normalized)的假定。
  • Composed form: 加了標音號(diacritic; tone mark)後的形式。例如goá, tâi-uân等等。Composed form有幾個麻煩的地方:(1) Unicode的問題,此問題已有許多人探討過,在此不表、(2) 正確的調號標示位置。例如早期「白話字」(pe̍h-ōe-jī)一詞,「白」的七聲號是標在母音 “o” 上面,然而近年來隨著音響律(sonority rules)共識形成,”oe” 一字的調號應該標在 e 上(oē,寫成台羅為uē),(3) 是否應容許「錯誤」的寫法(例如撰寫語言教科書,或探討歷史文獻時,諸如上述ōe/oē的例子)。
  • Normalized form: 即經過了正規化(normalization),調號擺放回正確位置的形式。我們內部文件裡偶爾會有normalized composed form或canonical form這種寫法。另外如前所述,query form永遠是normalized,因此就沒有normalized query form這種說法了。

在C++版本的lib-formosa中,HoloVowel跟HoloSyllable是兩個不同的單位。在內部表示(internal representation)上,我們允許各種音節形式(例如 “áéééì” 也是可被接受的);我們用各種輸出函數來得到最後的形式(query form, composed form in POJ/TL)。這一個設計上的複雜度也是我們暫不考慮改用Ruby重寫這一部份程式碼的原因。

Ruby Tuesday: RSpec; 7/31在OPCafé

兩週一次的Ruby Tuesday,本週二(7/31)如常在新竹的OPCafé舉行。iHower將繼續為我們帶來Ruby/Rails testing所需要的各種知識與工具。本週承繼上週的主題,並將介紹RSpec這個工具。歡迎大家參加。:)

並非「藝術是長的」

相當喜歡的一句名言是Seneca的”ars longa, vita brevis”。雖然很多人以其直譯”art is long, life is short”來謂藝術的生命長於人壽。但事實上追其語源,是Hippocrates對醫者的忠告:”ho bios brakhys, hê de tekhnê makrê” (ὁ βίος βραχύς, ἡ δὲ τέχνη μακρή)。

事實上,原文的 tekhnê (τέχνη,也就是 TeX 的語源啦)指的是「醫學的技術」。所以此處Hippocrates並非在感時傷懷,而是在告誡,技術的學習是無止盡的,而從業的困難重重(Wikipedia上的文章列舉了這些困難)。

多年前一位才氣極高的朋友(此人後來有拿到台灣重要的文學獎)曾說,”ars longa vita brevis” 最適切的翻譯莫過,「生也有涯,知也無涯」。不過如果窮盡Hippocrates的原文精神,則此種翻法恐怕也不妥。畢竟那後面可還有一句「以有涯隨無涯,殆已」。這兩者的精神,恐怕就差很多了吧。

因見到這篇日人文章提及此事,記之。另外也因此學到了原來 “crisis” (κρίσις, krisis) 的原意是「決定、決斷、判斷」。

7/17 Ruby Tuesday的投影片和錄音檔(更正URL)

感謝 iHower 為大家帶來精采的 “Rails Testing”,而且據稱這只是上集,且待兩週後的下回分曉!也謝謝大家熱情參與。:)

iHower的投影片可以在此處下載(轉自iHower’s blog)。現場錄音則可從此處下載 (43′ 55″)。

下一回Ruby Tuesday,iHower將繼續主講Rails testing、BDDRSpec。7/31晚上八點在OPCafé見!

Quote of the Day (and All Times)

Simplicity, simplicity, simplicity! I say, let your affairs be as two or three, and not a hundred or a thousand; instead of a million count half a dozen, and keep your accounts on your thumb-nail. In the midst of this chopping sea of civilized life, such are the clouds and storms and quicksands and thousand-and-one items to be allowed for, that a man has to live, if he would not founder and go to the bottom and not make his port at all, by dead reckoning, and he must be a great calculator indeed who succeeds. Simplify, simplify.

– Henry David Thoreau, Walden. Emphasis mine.

Ruby Tuesday: 7/17 (二) 在新竹 OPCafé 新址

這週二的 Ruby Tuesday 我們請到了iHower來為主講 Rails testing 。

時間是 7/17 星期二 20:00-22:00,地點在OPCafé 新址(新竹市光華街95巷3號,地圖)。

有興趣瞭解 Rails testing 的朋友請不要錯過。:)

用Python + OpenVanilla寫輸入法

繼Ruby-based OpenVanilla filter module後,OV和script language相關的進展又一彈:用Python撰寫輸入法。

orcas先前先是幫OpenVanilla撰寫了Python-based、可動態載入的filter module。後來又開始研究用SWIG來包裝OV的C++物件,讓Python也可以拿來撰寫輸入法。於是我們有了以下的code

# -*- coding: utf-8 -*-
import sys
import OVIMPython

class PYIMTestContext(OVIMPython.OVInputMethodContext):
  def keyEvent(self, keycode, buf, candidate, srv):
    char = chr(keycode.code())
    buf.append(char).update()
    srv.notify(char)
    candidate.show()
    candidate.append(char).update()
    return 0

class PYIMTest(OVIMPython.OVInputMethod):
  def identifier(self):
    return "OVIMTest"

編譯方法

目前Python-based input method module僅能在OS X上編譯。請先安裝SWIG(可用port安裝),然後使用最新的OV trunk,然後在OVIMPython目錄下,執行 make ; make DSTROOT=~ install 即可將編完的 OVIMPython 安裝到 ~/Library/OpenVanilla/0.7.2/Modules 中。

使用 Intel Mac 的朋友可能得看一下 Makefile 第三行,把中間那一大串關於 PowerPC 的字樣,改成你 build/ 目錄下(會在第一次 make 後出現)的某個名字。如何自動化處理 Makefile 的字串代換會是建立此類型模組的build system的關鍵所在。

Enjoy!

關於接下來的 Cocoa Tuesday

7/10 是星期二,照之前的慣例,是辦 Cocoa Tuesday ,不過一方面因應 OP Café 正在搬家,二方面我也在思考 Cocoa / Mac development 在台灣可以怎麼辦(也許應該併入 HappyDesigner,以後只講 web development,以因應 iPhone ? XD),還有就是想邀請台灣其他的 Cocoa 開發者──甚或是居住在台灣、有從事 Cocoa 開發的外國朋友──,這些在準備上,跟 Ruby Tuesday 「比較馬上就有東西可以講」相比,是的確要花多一點力氣。

所以簡言之,Cocoa Tuesday 再延後一次。也許以後會改成每月一次也未可知。

7/3 (二) Ruby Tuesday 在竹北達利

7/3 (二) 的 Ruby Tuesday,晚上 20:00-22:00 在竹北達利咖啡店。分享的主題為 Markaby 。

竹北達利咖啡店的地點在竹北市建國街37號,電話為03-554-3665。

活動更新:投影片可從這裡下載

Ruby與輸入法的第一步:用Ruby撰寫OpenVanilla filter模組

(更新:目前放入 trunk 的原始碼,還加入了「正體中文用詞轉簡體中文用詞」的功能。先前 OV 的簡繁轉換模組僅能做「字」的轉換,這個 filter 能做簡單的「詞」轉換。使用 script language + regex engine 來寫輸入法相關模組,果然是輕鬆快速許多的)

有時候跟朋友聊天,會有意外的收獲。

最近在研究embedded Ruby,也就是將Ruby放進用C/C++撰寫的程式中使用。其中最重要的計劃,就是想用Ruby來撰寫OpenVanilla的輸入法模組──如果這件事可行,將來撰寫、測試輸入法的難度,又可以比現在再簡單些,同時在演算法設計和資料結構/架構上,也會比現在用C/C++來撰寫來得輕鬆許多。

但是一想到要橋接(bridge)OpenVanilla的輸入法架構,就覺得有很多要先想好的事,有點複雜。

結果因為momizi指出我blog上的錯字,我回應說:「orz… 我需要我的常見錯字filter」──我常把「開催」(日文kaisai,舉辦的意思)寫成「開摧」、「間諜」寫成「間碟」──嗯,OpenVanilla filter不正是OV模組中最簡單的一種嗎?

於是,用Ruby來撰寫OV filter,並以此做為proof of concept的想法就出現了。

是的… 於是我們有了第一個用Ruby寫成的OV模組(更詳細的原始程式碼在這裡):

class OVOFRubyFilter
  def process(str)
    # do some brain-dead, 火星文-style substituion
    str.gsub!(/ㄅ/, "吧")
    str.gsub!(/ㄇ/, "嗎")    

    # fix lukhnos's typo
    str.gsub!(/開摧/, "開催")
    # ...

    # returns the string
    str
  end
end

這個模組目前暫時稱為OVOFRubyBased,裡面使用了embedded Ruby interpreter,會載入一個名為ov_ruby_filter.rb的程式。

只要編譯好這個模組,日後只要修改 .rb 就可以改變 filter 行為。目前的 build system 暫時只支援 OS X 10.4 ,但只要修改 Makefile 應該就能在其他版本的 OS X 或平台上使用(唔,不過 filter 支援,好像目前只有 OS X 版最完整?)。

另外就是,目前還沒辦法像Ruby on Rails的dev mode那麼神,只要一改.rb,行為就改變。這個應該可以當做一個to-do item。

這個proof of concept完成後,接下來則可以著手輸入法的部份,未來還可能架構一整個subsystem,用Ruby來載入一整組名為ov_*_input_method.rb或ov_*_filter.rb的模組群。這樣就可以完全用Ruby來寫輸入法了。不過,如何跨平台deploy,恐怕也會是個大問題。

越來越好玩了。

編譯方法

請使用最新的OpenVanilla原始碼,然後進 trunk/Modules/OVOFRubyBased/ ,執行:

make ; sudo make install

然後開啟新應用程式,啟動OVOFRubyBased這個filter module,就可以開始了。

不想把模組安裝在/Library/OpenVanilla下的朋友,可以把OVOFRubyBased.dylib及OFOFRubyBased/這目錄一併複製到~/Library/OpenVanilla/0.7.2/Modules/下,這樣就可以在不sudo的情況下修改ov_ruby_filter.rb,不過如上所述,每次改完,還是得開新應用程式才能生效。至少還是比需要重新編譯好多了。:)