siroemk’s blog

フィヨルドブートキャンプで学んでいます(2020/11〜)

Rubyで点字メーカープログラムを書いた

f:id:siroemk:20211210144921p:plain

この記事はRubyプログラミング問題にチャレンジ! -改訂版・チェリー本発売記念-のカレンダーの10日目の記事です。

qiita.com

フィヨルドブートキャンプで学習中のsiroです!ちょうどフィヨルドブートキャンプでRubyの課題を終えたとこだったので点字メーカープログラムにも挑戦してみます💪 

目次

こちらが作ったプログラムです

こちらがプルリクのリンクです。

https://github.com/JunichiIto/tenji-maker-challenge/pull/18

今回のアドベントカレンダーで指定されていた、点字プログラムの仕様通りに作りました。

  • ✅ 対応しているもの
    • あいうえお、かきくけこ、(省略)、やゆよ、らりるれろ、わ、ん
  • 🙅‍♀️ 対応していないもの
    • を、促音、濁音、長音、半濁音、拗音・拗半濁音など
    • 異常系の入力

テストもパスしています。

f:id:siroemk:20211210162723p:plain

1. 仕様を確認する

ここからは点字プログラムを作るにあたって、調べたことを書いていきます。まずは点字の規則や点字メーカープログラムの仕様を確認します。

点字について知る

ロジックを考える前に点字について調べました。点字は縦3点が2列並んだ6点を1マスとして文字を表します。

f:id:siroemk:20211210161526p:plain

この1マスで表すことができるのは63通り*1です。日本語の場合、濁音や半濁音を考慮すると63パターンを超えてしまい、1マスでは表現することができません。そのため、濁音などは2マスで表現します。 今回のプログラムは濁濁音や半濁音を考慮せずに、主に五十音に対応するので、1マスで表現することを前提に作りました。

いくつかの点字の資料を読む限り、点字の世界では6つの点を以下の並びの数字で表すようです。

f:id:siroemk:20211210144620p:plain

例えば、「"あ"は1の点、"か"は1と6の点」と表現します。

f:id:siroemk:20211210144921p:plain

この数字は点字の世界では共通する表現のようなので、プログラム上でこの数字を活用したいと思いました。

また、点字には母音(AIUEO)は1,2,4のどれかの点になり、子音(KSTNHMR)は5,3,6の点になります。

f:id:siroemk:20211210161603p:plain

例えば、"か(KA)"の場合、Aは1の点、Kは6の点なので、それを組み合わせて、1と6の点で「か」という文字を表します。

f:id:siroemk:20211210161649p:plain

以上のことを踏まえて、今回のプログラムで関係ありそうな点字の規則は、

  • 規則1: 母音(AIUEO)は1、2、4の点で表す
  • 規則2: 子音 + 母音の組み合わせ(KAなど)は、子音の点(3、5、6)と母音の点(1、2、4)を組み合わせることで点字を作る
  • 規則3: YA YU YO WA N だけ例外

こんな感じかなと読み解きました。

点字の詳細はこちらのページから確認しました。

点字メーカープログラムの入出力を確認する

今回のプログラムは以下のように入出力します。

tenji_maker = TenjiMaker.new
puts tenji_maker.to_tenji('A HI RU')
#=> o- o- oo
#   -- o- -o
#   -- oo --

見ての通りですが、

  • TenjiMakerクラスのto_tenjiメソッドに文字列を引数として渡してputsすると、点字が出力される
  • 点字の凸をoで表す
  • to_tenjiメソッドの引数には平仮名ではなく、ローマ字(訓令式)を渡す
  • 複数の文字を引数として渡す場合、文字の間を半角スペースで区切って渡す
  • 複数の文字を引数として渡された場合、点字は1マス(縦3点が2列)の間を半角スペースで区切って出力する

このようなことが言えると思います。

2. ロジックを考える

仕様や点字について確認が終わったので、ロジックを考えます。

どうやって与えられた文字列を点字にするか

点字の数字から点字の文字列を作ろうと考えました。

例えば、「A HI RU」という文字列の場合、Aは1の点、HI は1, 2, 3, 6の点、RUは1, 4, 5の点で表せます。この数字からまずは点字用の文字列('o-----')を作り、それを出力用に体裁を整える作戦でいこうと考えました。

まず、文字から点字の数字を得なくてはいけません。

そこで、

  • 母音と子音と特定の文字(YA YU YO WA N)は定数にする
  • KAなどの"子音+母音"で作る文字列は定数から数字を組み合わせて作る

という方法で文字から数字にすることにしました。

文字から数字を得ることができたので、次はその数字を利用して点字の文字列(character)を作ります。

# HI のcharacterを作ってみる。HI は 1, 2, 3, 6の点

numbers = [1, 2, 3, 6]
puts build_character(numbers)

def build_character(numbers)
  default_characters = ['-', '-', '-', '-', '-', '-']
  numbers.each { |number| default_characters[number - 1] = 'o' }
  default_characters.join
end

# 結果 > 'ooo--o'

これで数字から'ooo--o'というcharacterを作ることができました。ただ、この時点では'123456'という並びになっています。

このプログラムの出力は'123456'という並びではなく、

14
25
36

という並びにしなくてはいけません。

そこで、このようなbuild_rowを作って、[1列目, 2列目, 3列目]という配列を作ります。

def build_row(character)
   first_row = character[0] + character[3]
   second_row = character[1] + character[4]
   third_row = character[2] + character[5]
   [first_row, second_row, third_row]
end

p build_row('ooo--o')
# => ["o-", "o-", "oo"]

良さそう...!!

でも、これが複数文字になると

# ["A", "HI", "RU"]
#点字の数字に変換して
[[1], [1, 2, 3, 6], [1, 4, 5]]

#点字characterに変換して
["o-----", "ooo--o", "o--oo-"]

# [first_row, second_row, third_row]という配列に変換して
[["o-", "--", "--"], ["o-", "o-", "oo"], ["oo", "-o", "--"]]
# あれ...
# [[1行目, 2行目, 3行目],..]となっている

複数文字を出力するときは1行目を出力して改行し、2行目を出力して改行し、3行目を出力することになります。今のままだと文字ごとに行が分かれてしまっているので、表示がくずれてしまいます。

そこで[[1行目, 2行目, 3行目],..]ではなく[[1行目], [2行目], [3行目]]という配列にしたいと考えました。

Rubyにはtransposeという行と列の入れ換えるメソッドがあるので、こちらを使って表示列の配列に変換しました。

Array#transpose (Ruby 3.0.0 リファレンスマニュアル)

# [[1行目, 2行目, 3行目],..]になっている
array = ["o-", "--", "--"], ["o-", "o-", "oo"], ["oo", "-o", "--"]
p array.transpose

# => [["o-", "o-", "oo"], ["--", "o-", "-o"], ["--", "oo", "--"]]
# [[1行目], [2行目], [3行目]]になっている

この配列に半角スペースを入れたり、改行させたりして完成です🎉 

クラスを作る上で、少し動きを変えたりしているところもありますが、大まかな流れはこんな感じで作りました。

クラスの構成

今回は3つのクラスで構成しました。

  • TenjiMaker
  • TenjiFormatter
  • TenjiCharacter

TenjiCharacterクラスで文字を'ooo--o'に変換するところを、TenjiFormatterクラスではTenjiCharacterクラスで作った文字列'ooo--o'を出力用に整える仕事をしています。

工夫したところ

工夫したところは、文字列を作るクラスと体裁を整えるクラスで分けたところです。分けたことで出力の体裁に変更があったときに修正がしやすいのではと思いました。

懸念しているところ

今回のプログラムだと問題ないのですが、濁音などの機能追加をする場合、1文字で1マス使う文字と1文字で2マス使う文字が現れるので、ロジックの中で多重配列になり扱いにくいのでは...と思っています。

本当は2マス使う文字も「ここを直せばすぐできます!ほらわかりやすいでしょ!」みたいなコードにしたかったのですが、そこまで辿り着けませんでした。

3. 作り終えて

伊藤さんへメッセージ

チェリー本2版の発売おめでとうございます🎉

チェリー本にはいつもお世話になっています。早速2版を読んでみましたが、初心者が躓きやすいところや勘違いしやすいところに丁寧に解説が付いていて、さらにパワーアップしているなと感じました。1版と2版を読み比べるのが楽しいです!笑

これからもお世話になります!

さいごに

実はここまでつらつら書いてますが、数日前まで、他の方のコードと自分のコードを見比べて「自分が書いたしょぼいコードを世に出して良いのだろうか...」みたいな気持ちになっていました。

そんなことをフィヨルドブートキャンプの分報や日報などでボソボソ呟いていたら、ワラワラと人が集まってきて「動くコードを書いて記事を公開するだけでエラい」「挑戦しただけで100点」と励ましていただきました。ありがとうございました。なんとか公開できて一安心です。

アドベントカレンダーに登録したときは "かっこいいロジックを書いて自信満々で公開する予定" だったので、あまり自信がなく悔しさ全開なんですが、他の方の知識や技を取り入れて、いつか強いプログラマーになりたいです。精進します。

参考

プロを目指す人のためのRuby入門[改訂2版] 言語仕様からテスト駆動開発・デバッグ技法まで (Software Design plus)

全視情協:点字とは - 点字のしくみ

点字を数学的に読み取る

*1:6つの点で単純に表せるのは64通りだが、全ての点で凸がないパターンは視覚障害者にとっては文字として読み取ることできないので63通りになる