プルリクエストのリンク
ロジックの解説
- まず、ローマ字を0〜63のコード列に変換し、コード列を整形して出力する方針としました。この(codesの)整形は次で行なえます(左上の点を最上位ビットとする)。
codes.map{|e|('%06b'%e).tr('01','-o').scan(/../)}.transpose.map{|e|e*' '}*"\n"
-
さてコード列ですが、訓令式ということは(ヂとヅとヲは厳密には訓令式と異なる形となるが)母音と子音を分離できるということです。そこでまず母音と子音の辞書を作成しました。複数の(点字)文字を使う子音もあるので、子音の辞書の値は配列としました。そのうえで、子音の最後の要素の値と母音の値についてビット和を取ったものを(その音素の)コード列としました。子音がYおよびWの場合は別の母音の辞書を使うようにしました。母音を一番下まで下ろす際に、母音によってシフト数が異なるためです。
-
撥音に関して、実用性の観点から'SI NN I'と'SI N I'を区別したかったので、スペースは無視して連続で拾えるようにしました1。
-
その上で、全体の流れはおおまかに以下のとおりとなります。
- 子音が出現したら例外を除きバッファに溜めておく
- 母音が出現したらバッファを子音としてコード列に変換
- 同じ子音が連続して出現したら、
- Nであれば「ん」を出力してバッファを空に
- バッファの子音が2文字(以上)であったら(KYなど)エラー
- それ以外であれば「っ」を出力(バッファはそのまま)
- (撥音の事情があったため、せっかくなら促音にも対応しようということとなりました。)
- 変換終了時、バッファに子音が残っていれば、Nであれば「ん」を出力、それ以外ならエラー
コードのアピールポイント
- を、促音、長音、濁音、半濁音、拗音・拗半濁音に対応しています。
- ヂ、ヅ、ヲはdi/du/woで、これだけは訓令式でないのでご注意下さい
- ただし「こうもり」を 「こーもり」に変換するようなことは自動でやってくれないので正しく入力する必要があります
- (あとは助詞の「へ」と「は」とか)
- また、ツァ行(TS)、ファ行(F)やヴァ行(V)にも対応していますが、訓令式ベースなのであくまでおまけということでお願いいたします。
- ヘボン式でないので、TSUと入れるとツゥのように変換されてしまいます。
- TH行とかTW行とかはほとんど例外の扱いなので対応していません。
- そこまで例外が伴うなら全音素を辞書にするアプローチを取るべきですが、必須要件ではない上にプログラムがシンプルでなくなるということもあります。
- 半角スペースは入っていてもいなくても構いません(というかこうしないと促音に対応できない)。
- 文字列かどうかのチェックはしていませんが、「each_charに反応しブロックの引数が1文字のStringである」オブジェクトを作ることはまずありませんが不可能ではないので、あえて余地を残しています。
- スタンドアローンスクリプトとしても実行可能としています。
- Braille Patterns Unicodeを出力する機能があります。
伊藤さんにメッセージ
CodeIQではありがとうございました。今回もよろしくお願いいたします。
感想
- 実は点字の仕組み自体はポケモンルビー・サファイアのレジイベント攻略時に把握していました。 もう18年も前なのね。
- さらにローマ字からカタカナに変換する関数を他のCodeIQの問題で作成済みだったので、テストコード通過には実は30分もかかっていません…。
懺悔
- Advent Calendarが終わったから言えることですが、数値を2進数リテラルで書いたほうがいいと思ったのは(当然こちらで答案を作ったあとですが)他の方の答案を拝見してのことでした。
- VowelsDownは「最下段まで下ろした母音ですが」、なぜ最下段まで下ろすのかのコメントは書くべきだったと思いました。
- codes_to_tenjiの説明があっさりしすぎているのはそのとおりで、これは2日目の記事を見て気づきましたが後の祭りでした。
- スペースのハンドラを空欄にしたのは故意でした。すみません。理由としてはスペースを出力するようにすると仕様を外れてしまうためというのがありました。
-
撥音に対応するのが要件でなければ促音のことも考えなくてよいはずなので、シンプルなプログラムになったとは思います…。 ↩