Rubyで点字メーカープログラム作成にチャレンジしました！ 〜Qiita Advent Calendar 2021〜

![はてなBlog20211208.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1652831/a220bfa4-2a30-838c-41c2-abfec135c5ff.png)
## :cherries:この記事について
この記事は、伊藤 淳一(@jnchito)さんによる[Qiita Advent Calendar 2021「Rubyプログラミング問題にチャレンジ！ －改訂版・チェリー本発売記念－」](https://qiita.com/advent-calendar/2021/ruby-challenge)の8日目(12/8)の記事です。

## :cherries:自己紹介と参加した理由
はじめまして、[paru](https://twitter.com/Paru871)と申します。今回Qiitaへの初投稿となります。
2021年4月より[フィヨルドブートキャンプ](https://bootcamp.fjord.jp
)というオンラインプログラミングスクールでRubyを初めとするプログラミングとwebエンジニアになるための知識と技術を学んでいます。
Rubyに出会い学び始めて8ヶ月が経ちました。
まだ超初心者ですが、毎日スクールの仲間たちとの輪読会で親しんでいるチェリー本とメンターの伊藤さんに、これまでの学びへの感謝の気持ちを表したいと思い、今回の企画への参加を思い切って申し込みました。

## :cherries:プルリクエスト

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

※テストコードの追加はありません。

## :cherries:点字について知る
点字についての知識を全く持っていなかったので、まずはじっくりと点字の構成、成り立ちについて学び、多くのひらがなは母音＋子音の組み合わせで点字として表示ができるということを知りました。
![スクリーンショット 2021-12-07 16.10.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1652831/e3529917-69e4-4101-366b-74f0e38fb194.png)
[全視情協:点字とは \- 点字のしくみ](http://www.naiiv.net/braille/?tenji-sikumi)より

今回のプログラムでは、変換する文字の対象は
- あいうえお、かきくけこ、（省略）、やゆよ、らりるれろ、わ、ん
と指定されてかなり易しくしていただけているのですが、上記の母音＋子音で表現する原則とは違う表記をする文字があります。
それが下記の５つ、「や、ゆ、よ、わ、ん」です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1652831/80f04c00-d1f3-b70a-4b75-d3011aa850c3.png)

## :cherries:プログラミングの進め方
上記のことを踏まえて、今回は文字列と配列を使いながらプログラミングを進めていくことにしました。
- 点字の「●」になっている場所を下記のような数字(インデックス)で認識します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1652831/dcee8990-6129-3d12-0b7a-0d7b63565575.jpeg" width="200px">
- そして、母音と子音の組み合わせの原則で表現できる文字については、下記の図のように●になる数字をテーブルに定義することにします。
![1_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1652831/da1298e7-fb28-7d33-cbb2-f0560bcbbb34.jpeg)
- 特別対応が必要な5文字は別のテーブルで数字を定義しておきます。
![2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1652831/50fe529f-8532-aeee-c960-511dc55c0a92.jpeg)

こんな感じで、TenjiMakerクラスの一番上にテーブルを定義しました。
値の配列の数字が点字の「○」になる部分を表しています。

```ruby:
  NORMAL_LETTERS = {
    'A' => [0],
    'I' => [0, 2],
    'U' => [0, 1],
    'E' => [0, 1, 2],
    'O' => [1, 2],
    'K' => [5],
    'S' => [3, 5],
    'T' => [3, 4],
    'N' => [4],
    'H' => [4, 5],
    'M' => [3, 4, 5],
    'R' => [3]
  }.freeze

  SPECIAL_LETTERS = {
    'YA' => [1, 4],
    'YU' => [1, 4, 5],
    'YO' => [1, 3, 4],
    'WA' => [4],
    'NN' => [3, 4, 5]
  }.freeze
```



それでは、作成したプログラムのロジックを説明していきます。
## :cherries:ロジックの解説
### メインロジック
```ruby:
def to_tenji(text)
  text_array = text.split.map(&:chars)
  braille_array = convert(text_array).map do |element|
    @braille_letter = +'------'
    make_braille(element)
    @braille_letter.scan(/.{1,2}/)
  end
  braille_array.transpose.map { |two_degits| two_degits.join(' ') }.join("\n")
end
```
(１ステップずつ解説)

```
text_array = text.split.map(&:chars)
``` 
入力された文字列を1文字ずつ、そして母音と子音に分割して配列に入れます。
(例) `'A HI RU'`　→　`[["A"], ["H", "I"], ["R", "U"]]`

```
braille_array = convert(text_array).map do |element|
  @braille_letter = +'------'
  make_braille(element)
  @braille_letter.scan(/.{1,2}/)
end
```
- `convert`というprivateメソッドで上記のアルファベットの配列を数字情報に変換します。(メソッドの解説は次の項に書いています。)
(例) `[["A"], ["H", "I"], ["R", "U"]]` → `[[0], [4, 5, 0, 2], [3, 0, 1]]`

- できた配列から１要素ずつ取り出し、`make_braille`というprivateメソッドで、基本となる文字列"------"の該当部分を'o'に変換します。(メソッドの解説は次の項に書いています。)
(例) `[4, 5, 0, 2]` → `"o-o-oo"`

- その文字列を2文字ずつに分割して配列を作成して新しい配列を作ります。
(例) `"o-o-oo"` → `["o-", "o-", "oo"]`
    - ここでは文字列を分割後に配列を作りたいためscanメソッドを使用しました。正規表現は `.scan(/.{1,2}/)` を使いましたが、たった2文字なので `.scan(/../)`の方が簡単だったと後で気づきました。

- 全入力文字の処理が終わるとこんな配列が出来上がっています。
(例) `[["o-", "--", "--"], ["o-", "o-", "oo"], ["oo", "-o", "--"]]`

```
braille_array.transpose.map { |two_degits| two_degits.join(' ') }.join("\n")
```

- 最後に、配列を行列と見なして行と列を入れ替える`transpose`メソッドを用いて、スペースと改行を付加して出力します。これで出来上がりです！！

```
(出力結果)
o- o- oo
-- o- -o
-- oo --
```
### privateメソッド2つ
#### convertメソッド

```ruby:
def convert(text_array)
  text_array.map do |first, second|
    second ||= first
    SPECIAL_LETTERS[first + second] || (NORMAL_LETTERS[first] | NORMAL_LETTERS[second])
  end
end
```
このメソッドではアルファベットの配列をテーブルを参照して数字情報に変換します。　　
(例) `[["A"], ["H", "I"], ["R", "U"]]` → `[[0], [4, 5, 0, 2], [3, 0, 1]]`

まずは子音`first`と母音`second`に引数の配列の1要素を代入します。
##### (Nilガード)
もしも母音のみまたは"N"の場合は`second`がnilになるので、　`||=`のnilガードで`first`の文字を`second`に入れておきます。
##### (短絡評価と配列の和集合)
そして、まずはSPECIAL_LETTERSのテーブルで[first + second] をキーにして特別な5文字を探し、当てはまったらその値を返します。
SPECIAL_LETTERSで見つからなかったら、NORMAL_LETTERSテーブルから子音と母音をキーに値を取得し、配列の和集合`|`で1つの配列にします。



#### make_brailleメソッド

```ruby:
def make_braille(element)
  element.each do |number|
    @braille_letter[number] = 'o'
  end
end
```
このメソッドでは基本となる文字列"------"に対して、数字で指定された部分を'o'に変換します。
(例) `[4, 5, 0, 2]` → "------"の4,5,0,2番目を変換 → `"o-o-oo"`

### おまけ

```ruby:
if __FILE__ == $PROGRAM_NAME
  text = ARGV[0]
  puts TenjiMaker.new.to_tenji(text)
end
```
要件には指定されていませんでしたが、コマンドから入力されたローマ字を点字への変換ができるように直接このファイルを実行できるコードを加えました。

```ruby:
[実行例]
❯ ruby lib/tenji_maker.rb 'A HI RU'
o- o- oo
-- o- -o
-- oo --
```

## :cherries:点字メーカープログラムを作成した感想
チェリー本をあちこち読み返しながら楽しく作成することができました。
今まで全く馴染みのなかった点字の仕組みを知ることができたことも大きな収穫です。
とても良い経験をさせていただきありがとうございました！
点字にとても興味が湧いたので、皆さんが作られたプログラムをじっくりと読ませていただきながら、今後全文字対応できるプログラムを自由研究で書いてみたいなと思っています。

## :cherries:伊藤さんにメッセージ
チェリー本改訂第2版の出版おめでとうございます！
今後の一番の愛読書になること間違いなし！です〜:blush: