点字メーカーを作ってみた! ~チェリー本改訂版発売記念アドベントカレンダー企画~

##はじめに
この記事は[Rubyプログラミング問題にチャレンジ！ －改訂版・チェリー本発売記念](https://qiita.com/advent-calendar/2021/ruby-challenge)の点字メーカープログラム作成したので、それについての解説記事です。

* 作成した人
 * Ruby歴半年、今年6月からエンジニア転職を目指して毎日働きながら学習継続中
 * 新卒で地元のメーカーに就職した社会人2年目の23歳
<br>
<br>

* なぜこちらの企画に参加したのか
 * 伊藤さんのコードレビューを受けれるかもしれないから
 * チェリー本を愛用しているため、形ある貢献がしたかったから
 * Qiitaでアウトプットがしたかったから（初記事です）
<br>
<br>



* 完成したプログラムのプルリク↓

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



##お題の概要
今回作成したプログラムは、入力されたローマ字に対応する点字をテキストで出力します。
以下が実行例です。

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

puts tenji_maker.to_tenji('KI RI N')
#=> o- o- --
#   o- oo -o
#   -o -- oo
```

- 点字の点には半角小文字の`o`を、出力されない部分には半角ハイフン`-`をつける
- 点字と点字の間は上の実行例のように、半角スペースで区切る
- ローマ字から点字への変換は以下のサイトに従う

http://www.naiiv.net/braille/?tenji-sikumi

![Screen Shot 2021-10-28 at 6.39.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7465/1c80d44b-ae0f-1153-b29d-d92be6965380.png)
*引用 [全視情協:点字とは \- 点字のしくみ](http://www.naiiv.net/braille/?tenji-sikumi)*

このお題の詳細は[Read Me](https://github.com/matsuken314/tenji-maker-challenge#%E4%BB%8A%E5%9B%9E%E3%81%AE%E3%81%8A%E9%A1%8C%E7%82%B9%E5%AD%97%E3%83%A1%E3%83%BC%E3%82%AB%E3%83%BC%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0)で詳しく書かれてあるので、ここまでにします。
それでは解説をしていきます。

##ロジックの解説
このお題を突破するには、大きく分けると以下の**3つの処理**が必要だと思いました。

| 番号 | 処理内容 |
|:---:|:---|
|**1** | 入力されたローマ字を読み取る処理　(どのローマ字が入力されたのか、何文字目なのか、何文字入力されたのか)|
|**2** | ローマ字を点字に変更する処理|
|**3**| 点字を出力する処理　(どの点字を入力するのか、何文字目に変更した点字を出力するのか、何文字点字を出力するのか)|


そしてこれに準じて、作ったメソッドは3つあり、
**1,3**の入力されたローマ字の読み取り、出力を担当する`read_and_output`
**2**の主に母音の処理を担当する,`judge_mother_text` 
**2**の母音以外のその他処理を担当する`judge_others_text`

本出力を行う`to_tenji`メソッドの中身は下記になります。

```ruby:tenji_maker.rb
 def to_tenji(text)
    read_and_output(text)
 end
```

全体のロジックを図解すると下記のような感じです。

![phonto 10.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1584967/992d2ffd-bde5-5fa6-3905-fbe43262ab8f.jpeg)


### ローマ字を読み取り、点字を出力する`read_and_output`メソッドについて
まずこのメソッドの**全体的な流れ**としては下記コードでコメントアウトした箇所のような形です。

```ruby:tenji_maker.rb
def read_and_output(text) 
#1.入力された文字を取得

#2.その文字を自作メソッド(judge_mother_text, judge_others_text)を用いて、ローマ字から点字に変換

#3.ローマ字から点字に変換した字を返す
end
```
<br>

そして下記の「何文字入力されたのか」と「どんなローマ字が入力されたのか」の処理によって、
`#1.入力された文字を取得`することができます。

```ruby:tenji_maker.rb
# 1.入力された文字を取得
def read_and_output(text)  
 nil_number = text.count(' ')  #半角の空白をカウント
 text_number = nil_number + 1  #文字数を取得
 text_array = text.split       #入力された文字ごとに配列の要素として格納　
（省略）...
end
```

例えば、「A HI RU」が入力されたときに
`text_number = 3`
`text_array = ["A" "HI" "RU"]`
という形になります。
なので、`text_array`は配列として扱われ、入力された文字列を配列として扱うことができるようになりました。
この時点で、下記のように入力された文字を1字ずつ扱うことができます。

```ruby
p text_array[0]
#"A" 
```

<br>
次に入力されたローマ字を点字に変換する処理を行います。
そして、点字は下の画像(ちなみに「あ」)のように１文字が３行２列と決まっていて、
![スクリーンショット 2021-12-13 4.46.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1584967/3c769ac6-8767-a50d-9ecf-75a3156e7f7a.png)
2文字以上になると,３行２列が空白を通して、連なっていきます。
![スクリーンショット 2021-12-13 5.14.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1584967/829a4c4a-1100-692f-b655-ee4194dcfb5d.png)
*引用 [全視情協:点字とは \- 点字のしくみ](http://www.naiiv.net/braille/?tenji-sikumi)*

上記の性質を利用して、点字用の変数配列`@tenji_str1`,`@tenji_str2`,`@tenji_str3`を作成し、取得した文字数(`text_number`) × `--　` ( `one_letter`)を代入しました。


例えば「A HI RU」が入力された場合、各配列型変数に入る値は下表のようになります。1文字3列分です。なのでこの場合だと、「A」が[0,1,2],「HI」が[3,4,5],「RU」が[6,7,8]
また、**各文字の３列目には何も入ってないわけではなく、半角の空白**が入っています。


| 点字1文字「あ」 | [0] | [1] | [2] | [3] | [4] | [5] | [6] | [7] | [8] |
|:---|:---:|:---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
|`@tenji_str1`|- |- | |- |- | |- |-| |
|`@tenji_str2`|- |- | |- |- | | -|-| |
|`@tenji_str3`|- |- | |- |- | | -|-| |

以上の処理が、下記になります。


```ruby:tenji_maker.rb
#2.入力された文字を自作メソッド(judge_mother_text, judge_others_text)を用いて、ローマ字から点字に変換
def read_and_output(text)  
...（省略）

   one_letter = '-- '
   @tenji_str1 = one_letter * text_number #点字1行目
   @tenji_str2 = one_letter * text_number #点字２行目
   @tenji_str3 = one_letter * text_number #点字３行目

（省略）...
end
```


次に`each_with_index`で、入力された`text_array`に格納された配列型のローマ字を1字ずつループさせることで、ループ内で点字に変換していきます。

```ruby:tenji_maker.rb
#2.入力された文字を自作メソッド(judge_mother_text, judge_others_text)を用いて、ローマ字から点字に変換
def read_and_output(text)  
...（省略）

   text_array.each_with_index do |text, num|
    judge_mother_text(text, num * 3) #母音処理
    judge_others_text(text, num * 3) #母音以外を処理
   end

（省略）...
end
```

次にローマ字から変換した点字が代入された`@tenji_str1`,`@tenji_str2`,`@tenji_str3`を１つの文字列`string`として`read_and_output`の返り値とします。

```ruby:tenji_maker.rb
#3.ローマ字から点字に変換した字を返す
def read_and_output(text)  
...（省略）

   string = @tenji_str1.rstrip + "\n" <<  @tenji_str2.rstrip + "\n" << @tenji_str3.rstrip
   string
end
```
以上が入力されたローマ字を読み取り、点字を出力する処理を扱う`read_and_output`メソッドのロジック解説でした。

### 母音のローマ字を点字に変更する`judge_mother_text`メソッドについて
ここからはどうやって母音のローマ字を点字に変換したのかというロジックを解説していきます。
前提として、以下のようにすでに`-`が代入された状態の点字出力用の３つの変数が渡された状態で、処理を開始します。

| 点字1文字 | [i] | [i+1] | [i+2] |
|:---|:---:|:---:|---:|
|`@tenji_str1`|- (1番)|- (4番) | |
|`@tenji_str2`|- (2番) |- (5番) | |
|`@tenji_str3`|- (3番) |- (6番) | |

結論から始めると`case when`と`include?`を用いて、各ローマ字の母音が含まれている場合、指定箇所に`o`を入力する。といった処理です。

例をあげると、

```ruby:tenji_maker.rb
def judge_mother_text(text,number)
 i = number
    case 
    when text.include?('A') #textにAが含まれている場合に下記処理を実行
    @tenji_str1[i] = 'o'　#1番にoを代入

    ...省略

    end
end
```

Aが入力されたので、1番に`o`を代入。といった形で母音が含まれている文字ごとに`o`を指定位置に代入し、入力されたローマ字に準じて、点字に変換します。

### 母音以外のその他ローマ字を点字に変更する`judge_others_text`メソッドについて
ここからはどうやって母音以外のローマ字を点字に変換したのかというロジックを解説していきます。
**前提**として、点字の子音（母音以外）は母音(あ、い、う、え、お)との組み合わせでできます。

どういうことかというと点字の「あ,い,う」と「か,き,く」を見比べるとわかるのですが（下記画像参照）、か行は母音の点に6番の点を加えただけ。なので、
**母音 + 6番　= か行**

![スクリーンショット 2021-12-13 21.49.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1584967/88284367-96c4-aade-06da-0b69f0b87a93.png)
![スクリーンショット 2021-12-13 21.49.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1584967/a6046aed-916d-2e88-e8a2-be30eba75823.png)

この調子でサ行もいっちゃいましょう。
**母音 + 5番 + 6番　= さ行**
![スクリーンショット 2021-12-13 21.53.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1584967/83bb8b7f-e412-dc87-d6f9-35c48b4589f5.png)
*引用 [全視情協:点字とは \- 点字のしくみ](http://www.naiiv.net/braille/?tenji-sikumi)*

ということで子音は母音との組み合わせでできていることを説明しました。
そして、この法則をうまく利用したのが`judge_others_text`メソッド

`case`と[正規表現](https://www.ruby.or.jp/ja/tech/development/ruby/tutorial/040_regexp.html)を用いて、任意の字から始まっている２字のローマ字がある場合、指定箇所に`o`を代入する。といった処理です。

例を上げると、「K」から始まる2字のローマ字には6番に`o`を代入する。
もっと詳細に言えば「KA」が入力されている場合、`judge_mother_text`で「A」を点字に変換後、`judge_others_text`で「K」を点字に変換します。

以上のようなロジックで子音のローマ字を点字に変換する処理を行っていました。

```ruby:tenji_maker.rb
 def judge_others_text(text,number)
    i = number
    case text
    when /K\D/ #Kから始まる2文字の場合、下記処理を実行
    @tenji_str3[i+1] = 'o' #6番にoを代入
    ...省略

    end
end
```

##コードのアピールポイント


### 頑張ったところ
- 変換された点字を出力するロジックの実装
<br>

点字の文字数がどれだけでも3行であるという特徴（このプログラム課題上では）を活かして、`@tenji_str1`,`@tenji_str2`,`@tenji_str3`
の３つの文字列型変数を作り、最後にまとめて1つの文字列`string`にまとめて返したロジックは我ながらよく頑張って考え抜いた箇所です。


### 苦労したところ
- ローマ字を点字に変換するところ

「や行」,「わ」などの例外部分は特に母音の出力がデフォルトとは違うので、苦労しました。

### 工夫したところ
- splitメソッドを用いて入力されたローマ字の文字列を分割し、配列として扱ったところ


- 正規表現を用いて子音（母音以外）の条件分岐をしたところ


##全体を通しての感想
まだ使ったことのないメソッドを使ったり、
点字について深く知ることができたり、
Qiitaの記事で自分の書いたコードをアウトプットしたり、
初めてのこと尽くしでしたが、なんとかやり切ることができ、ホッとしています。

エンジニア転職に向けて、Rubyの総復習をこのプログラム課題を通してできたので、大変勉強になりました！

## 伊藤さんにメッセージ
チェリー本にはいつも大変お世話になっております。
またQiitaの良質な記事にもいつも助けられています。
これからも勝手ですがお世話になります！