SKK-JISYO.lisp
NL名古屋 - connpassでSKK辞書の話をした。 要約すると、特定の文字をエスケープするだけでEmacsLispが必要になってつらいという話だった。
Transiruで発表したので、発表時の音声等はこちらで聞ける。終盤早口になってしまったので恥かしい。声優吹き替えオプションが欲しい。
原稿
前提
自己紹介
こんにちは。 mzpです。 タイトルにあるように日本語入力システムとLispについて話したいと思います。日本語のNと、LispのLでNLです。
何の話?
一言で日本語入力システムといってもたくさんの種類がありますし、Lispにもたくさんの種類がありますね。
ので、具体的に言うと、今日はSKKとEmacsLispの話をします。LispといってEmacsLispを持ちだすのは微妙な気がしますが、まあLispと名乗ってるわけですしOKでしょう。
SKKとは
このSKKについてですが、これはだいぶ独特な日本語入力システムです。通常の日本語入力システムがやるような作業を利用者におしつけることで、高速かつ高精度の日本語変換を実現しています。
入力例
分かりづらいと思うので、入力例を見てみます。例として「今日は雨です」と入力してみる場合です。
普通の日本語入力システムでは「きょうはあめです」と入力したのち、スペースを押して、変換候補を表示します。お馴染みですね。
SKKでは、漢字変換を始める前にShiftを押します。
Kyou
そして、複数の単語をまとめて変換することはできないので、ここでスペースをおして候補を表示します。
ひらがなはそのまま入力すればOKです。特に変換は必要ありません。
ha
また漢字なのでShiftを押します。
Ame
そして、スペースを押して候補を表示し、確定させます。
そして、またひらがなはそのまま入力します。
desu
SKKの特徴
このように
- 単語ごとでしか変換できない
- 漢字に変換するかどうかを自動で判断しない
といった特徴があります。
一見使いづらいだけのように見えますが、変換する単語を完全に自分で制御できるので、慣れれば離れられなくなる中毒性があります。
SKKの実装
中毒性が高いソフトウェアのため、現在では様々なプラットフォームで利用できます。
- Windows: SKKFEP、CorvusSKK
- Mac: AquaSKK
- iOS: FlickSKK
- Linux: iBus-SKK, UIM-SKK, SCIM-SKK
- Vim: skk.vim
- Emacs: ddskk
このAquaSKKとFlickSKKは僕がメンテしてます。
SKKの歴史
元となったSKK(通称: 本家SKK)は、1987年に、東北大学教授(当時)佐藤雅彦によって開発されました。
この本家SKKは、Emacs上のプログラムとして開発されました。 つまりEmacsLispを用いて開発されていました。
いやー、Emacsはすばらしいソフトウェアですね。 実にすばらしい。
辞書ファイル
とEmacsを十分に褒めたところでつらい話をはじめます。
ひらがなを漢字に変換するためには、ひらがなと漢字の対応表が必要になります。この対応表は辞書と呼んでいます。
そしてこの辞書ファイルが闇を貯めこんでいます。
辞書ファイルの書式
辞書ファイルはテキストファイルになっており、各行が以下のような書式になっています。
なごや /名古屋;愛知/那古屋/
左から順に「見出し語」「変換候補」「アノテーション(要するに補足情報)」となっています。変換候補間は/で区切られていて、アノテーションとは;で区切られています。
自然な感じがしますね。
エスケープ
しかし、この書式をじっと見てると、いくつか使えない文字があることが気づきます。そう、変換候補として「;」や「/」を含むことができないのです。
例えば「owata」の変換候補として「(^o^;)/」 とかを登録したい場合はどうしたらいいでしょう。
対応方法1: エスケープ
いくつかの対応方法が考えられます。 例えば、文字列リテラルのように\でエスケープすればよさそうですね。
owata /\\(^o^\;)\//
対応方法2: 別の文字への置換
あるいは別の文字に置換してしまうのもいいでしょう。
例えば、"[ASCIIコード]"のような記法を採用すると、;のASCIIコードは0x3b、/は0x2fなので以下のようになります。
owata /\(^o^[3b])[2f]/
対応方法3: 全角文字
あきらめてよく似た文字を使うことで誤魔化してもいいです(?)。
owata /\(^o^;)//
いやよくないでしょ...。
正解
みなさんだったらどうしますか?ちょっとだけ考えてみてください。
....
考えましたね。 ではSKKがどうしたかを見てみましょう。
owata /(concat "\(^o^\059)\057")/
S式の文字列リテラルを使ってエスケープする、が正解でした〜。
まじかよ...って感じですね。
その他の便利機能
日本語入力システムを使ってると、いろいろ便利な変換したくなってきます。
例えば、
- todayを今日の日付に変換したい
- nowを今の時刻に変換したい
- 5feetをメートルに変換したい
- 元号を変換したい
などです。
これらはすべてEmacsLispを使って実現できます! すごい!
まじかよ...って感じですね。
さらにすごいことに以下のような変換もEmacsLispで実現されています。
- 画面幅いっぱいの線
まじかよ...って感じですね。
実装
Lispの実装 これらの機能は使う分にはまだいいんですが、実装者としては悪夢です。
つまりSKKを実装するには、EmacsLisp処理系を実装しなければいけません。 より具体的に言うと、AquaSKKとFlickSKKはボクがメンテしていく上で、ボクがEmacsLisp処理系を実装しなければいけません。
まじかよ...って感じですね。
SKK-JISYO.lisp
なげいていてもしょうがないので、現実を確認してみましょう。
実は「今日の日付」のような複雑なEmacsLispの式を含む変換候補は、通常の辞書とは別で管理されています。余談ですが、この別に管理されている辞書はLisp辞書と呼ばれていますが、このLisp辞書って単語もだいぶ愉快ですね。
そのため通常の辞書だけを利用すると割り切ってしまえば、フルセットのEmacsを実装者するのは避けれます。通常の辞書に残っているEmacsLispの式はconcatによる文字のエスケープだけなので、これに対応するだけで十分です。
実装状況
SKKの各実装がこれにどのように対応しているか見てみましょう。
AquaSKK/FlickSKK
AquaSKK/FlickSKKは割り切った対応をしています。外部の辞書ファイルに含まれるconcatについては何もしません。つまり変換するとこんな変換結果がでてきます。(例: dosv)
そして、ユーザが入力した内容した結果については、別の文字列に置き換えることでエスケープしています。先ほど述べた別の文字への置換のような形式です。
owata /\(^o^[3b])[2f]/
後者はともかくconcatがでてくるのは微妙なので直したいとは思っています。
ibus-skk/scim-skk
ibus-skk/scim-skkは、concatのみの実装をしていてソツがない感じになっています。
CorvusSKK
一方、CorvusSKKはここの実装をがんばっていて、いくつかの関数もサポートしています。すごいです。
その他
Lispの話はこれくらいですが、実は辞書ファイルにはまだいくつかの闇が存在しています。
辞書の並び順
これまでは辞書の各行の話をしてきましたが、これらの行は見出し語をキーとしてソートされています。いわゆる辞書順です。辞書ですからね。
このときの比較関数としてはEmacsLispの string< が使われます。....またEmacsLispか。
そしてこのstring<は文字コード順で文字列を比較します。 はい、ぞわぞわしてきましたね。
なんの文字コードだよ、って感じですね。SKKでは伝統的にはEUC-JPが使われますが、最近はUTF-8を使うことも多いです。というかボクは使っています。
UTF-8の比較というとアレがありますね。 濁点問題です。NFCとNFDという2種類の形式があります。濁点を合成された文字としてもつか、分解された文字としてもつかの違いです。
世の中的にはNFCを使うことが多いんですが、ボクのメンテしているApple系のプラットフォームだとNFDが主に使われてたりします。
どちらが正しいとか優れているとかはありませんが、並び順には影響するので注意深く扱う必要があります。ボクはドハマりしました。
きっついですね。
辞書のライセンス
SKKの辞書ファイルはGPLで配布されています。 フリーなソフトウェア!! 自由!!
...こういうデータファイルがGPLっていうのはどういう扱いなんでしょう。
wikipediaから引用すると、GPLはおおむね以下のことを許諾するライセンスです。
- プログラムの実行
- プログラムの動作を調べ、それを改変すること(ソースコードへのアクセスは、その前提になる)
- 複製物の再頒布
- プログラムを改良し、改良を公衆にリリースする権利(ソースコードへのアクセスは、その前提になる)
改変の許可と再配布はいいとして、実行というのは何になるんでしょうね。 あとテキストを読み取ってアレコレするのは動的リンク扱いで派生物扱いになったりするんでしょうか。
謎です。
AquaSKK/FlickSKKのメンテナとしてのポジショントークは、同梱であってリンクではない。そのためGPLである辞書ファイルを読み取ってあれこれするプログラムはGPLである必要性はない、です。
別にアンチGPLというわけではなくて、AppleのAppStoreとGPLの相性が悪いので、俺の書いたコードはGPLではない、ということにしとかないと色々と都合が悪いんです。