Haskellにも喋ってもらおう
弾さんのblogを見ていたら夏時間を支持するのは頭が春な人だけ なんて言うコラムが 出ていた。
年に2回も時計を合わせるの、めんどくせー。 時刻を人間の都合で勝手に進めたり 遅らせたりしたら、物理の根底が崩れちゃうよ。それに、時刻は同じものが2度と 現れないと言う性質を使って、唯一性を保証してる某プログラムは発狂しちゃうぞ。 (すんません。面倒なので、TZは無視って事で、プログラムしちゃいました)
海外に居た時、一度だけ、夏時間->通常時間へと変更の現場に立ち会った事がある。(ああ、その瞬間は 寝てました。)朝起きて、食堂へ行ったら、親父さんに、お前の時計は狂ってると、 からかわれた思い出があります。
面倒な事は、ごめんこうむりたい。いくら、adjkerntzがあると言っても、コンピュータ だって、面倒くさいぞ。それに、tzinfoのメンテも必要だし。
[sakae@nil /usr/src/share/zoneinfo]$ lv asia ..... # From Paul Eggert (1995-03-06): # Today's _Asahi Evening News_ (page 4) reports that Japan had # daylight saving between 1948 and 1951, but ``the system was discontinued # because the public believed it would lead to longer working hours.'' # Shanks & Pottenger write that DST in Japan during those years was as follows: # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule Japan 1948 only - May Sun>=1 2:00 1:00 D Rule Japan 1948 1951 - Sep Sat>=8 2:00 0 S Rule Japan 1949 only - Apr Sun>=1 2:00 1:00 D Rule Japan 1950 1951 - May Sun>=1 2:00 1:00 D # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone Asia/Tokyo 9:18:59 - LMT 1887 Dec 31 15:00u 9:00 - JST 1896 9:00 - CJT 1938 9:00 Japan J%sT
Haskellだって喋って欲しいぞ
暫く前に、GHCがメンテナンスリリースされて、ちょびっと版が上がった。いつもなら 直ぐに追従する所だけど、秋口に予定されてるメジャー版アップを思うと、このまま でもいいかなと。
まだHaskell修行中な身ゆえ、Bugを踏むというような事もなかろうかと。で、今まで Rubyやgauche君には喋ってもらっているので、今回是非Haskell君にも喋ってもらいたい なー、と思った次第。
大体、Haskellやってて、悩むのは IOがからんだ部分だけだもの。全開発時間の90% 以上は、IO xxx との、整合性調整に費やしている現状を是非とも打破したいのだ。 で、考えてみたら、喋るをやろうとすると、程よくIOが混じっている事を実感してる ので、練習には、いいかな。
部品の選定
改めて調べてみたら、Windows版には、putenvが無いのね。これは、Unix版との最大の違い かと、愕然としましたよ。嘆いてもしょうがないので、喋るデータを、引数で渡す jsay を使う事にする。これを使うと、データにスペースや<> を含められないと言う、制限 が出ちゃうけど、この際無視する。
次は、mecabとどうやって交信するかだな。データを送り込んで、結果をhaskell側に 戻すと言う難題が控えている。調べてみたら、本物のプログラマはHaskellを使うシリーズで 第14回 Haskellでメッセージ通信を使う利点 が、ヒントになりそうな事が分かった。
あと、もう一つ難題がある。それは、文字コードの変換をどうするかだ。極東の小さな 島国で、大手を振ってしつこく生き延びている、SJISを扱う事が出来るか。utf-8との 間で変換が出来なければ、この計画は頓挫する。
文字コード変換と言えば、そりゃ、nkfでしょと、古くからのunix userは考えてしまう。 Windows版のnkfが有るかと思って調べてみたら、 nkf.exe nkf32.dll Windows用 見つかったよ。手回しよく、Windows 7用動作報告ページまで、作られていて、 つくづくWindowsと付き合っていく大変さを実感した次第。まもなく始まるぞ、阿鼻叫喚な 戦争が!
取り合えず、喋れ
まずは喋る所から。先の本物の... を参考に、jsayとのインターフェースをrawSystemに 任せてみる事にした。だって、なるべく command.comなんて言う時代遅れの骨董品と 付き合う必要なんて無いと思ったからだ。
jsay :: String -> IO ExitCode jsay s = rawSystem "jsay" [s]
これは、簡単に書けた。gaucheで言う、run-process と考え方は一緒ね。引数は、listに まとめて渡すと。で、書いたのはいいんだけど、どうやって検証するかだな。取り合えず ghci を起動して実験してみよう。
*Main> jsay "12345" ExitFailure 105
105 って、「音声記号列に未定義の読み記号が指定された」と言うエラーか。ひらがなや カタカナを入れてみても、ghciはutf-8だと思っているので、やはり105しか返ってこない。 諦めムードで
*Main> jsay "<NUMK VAL=1234>" ExitSuccess
こんな事をしてみたら、喋っちゃったよ。こりゃ、一体どういう事? しばし考えて 合点がいった。commnad.comを経由させなかったので、文字列がそのまま、jsayに届いた のだ。こりゃ、いいわい。
次は、mainを実装してみっか。ファイルを受け取って、それを行に分解し、jsayに 渡せばいいんだ。難しい事は考えずに、向井さんの本から、mapM_ と言うのを 引っ張り出してきた。
main :: IO () main = do args <- getArgs cs <- readFile $ head args mapM_ jsay $ lines cs
head args ってのは、gaucheで言う所の、(cadr args) だ。これで、数字TAGを並べた ファイルからのデータを読み上げるようになった。
今度は、ひらがなも喋れ
数字(だけですが)喋り始めたので、気をよくして、今度は、ひらがな文も喋れる ようにします。これが完成すれば、選挙戦真っ只中のあの人の代役が務まるかしらん。
command.com嫌いになった私は、runInteractiveProcessを使ってみる事にしました。 GHCのページで使い方を調べるも、いまいちピンとこなかったので、サンプルを 探してみました。
iconvを外部プロセスとして呼び出して漢字コードを変換する ぴったりのものが見つかりました。このページを見たら、rubyに付属してるiconvうん ぬん なんて書いてあったので、自分のマシンにも入っているか調べてみたら、入って いたよ。よって、nkfを入れるのはやめて、そのまま、利用させて頂く事にしました。
結果、出来上がったのは、以下の通り
-- -*- utf-8 -*- -- speak japanese import System import System.IO import System.Process import System.Cmd import List iconv :: String -> String -> String -> IO String iconv from to s = do (input, output, _, _) <- runInteractiveProcess "iconv" ["-f", from, "-t", to] Nothing Nothing hSetBinaryMode input False hSetBinaryMode output False hPutStr input s hClose input hGetContents output s2u :: String -> IO String s2u s = iconv "SHIFT_JIS" "UTF-8" s u2s :: String -> IO String u2s s = iconv "UTF-8" "SHIFT_JIS" s jsay :: String -> IO ExitCode jsay s = rawSystem "jsay" [s] speak :: String -> IO ExitCode speak s = do utf <- s2u s sjis <- u2s utf jsay sjis main :: IO () main = do args <- getArgs cs <- readFile $ head args mapM_ speak $ lines cs
テストの為、sjis <--> utf-8 は、ラウンドトリップさせてますが、ここは、おいおい mecab を組み込んだ時に、変更する予定です。