銀座Rails＃41企画の「Rubyで点字メーカープログラム」を作ってみました

# はじめに
はじめまして、シモカワと申します。
プログラミングスクールの[フィヨルドブートキャンプ](https://bootcamp.fjord.jp/)で学習6ヶ月目となり、今回がQiita初投稿です。

本記事は、Qiitaで開催中の「[Rubyプログラミング問題にチャレンジ！ －改訂版・チェリー本発売記念－のカレンダー \| Advent Calendar 2021 \- Qiita](https://qiita.com/advent-calendar/2021/ruby-challenge)」の銀座Rails版の点字プログラムの問題を解いたときのお話です。

銀座Rails版Rubyプログラミング問題のGitHubのリポジトリは[こちら](https://github.com/JunichiIto/tenji-maker-challenge-for-ginza-rails)です。

# 動機
フィヨルドブートキャンプでメンターをされている伊藤淳一さんよりコミュニティ上で銀座Railsでの公開コードレビュー開催のご案内があり、面白そうだと思って参加することにしました。
本音は、Qiitaアドベントカレンダー2021のときに参加できれば良かったのですが、フィヨルドブートキャンプの方の[アドベントカレンダー](https://aim2bpg.com/ruby/178/)や[LT会](https://speakerdeck.com/aim2bpg/fjordbootcamp-lt10-5)が、同時期に重なったという理由で、今回、チャレンジすることにしました。

# 作ったコード
コードレビュー用に機能を省略した「通常バージョン」と、つい、欲が出て手を出した「辛口バージョン」の2種類を作りました。

[通常バージョン](https://github.com/JunichiIto/tenji-maker-challenge-for-ginza-rails/pull/9/files)
[辛口バージョン](https://github.com/aim2bpg/tenji-maker-challenge-for-ginza-rails/blob/modiy_tenji_maker_part_2/lib/tenji_maker.rb)

# ロジックの解説
## メインメソッド
メインメソッドの`to_tenji`では、
・まず、引数`text`をスペース区切りでローマ字1文字ずつに区切ります。
・次に、`make_tenji`というサブメソッドを使ってローマ字から点字を作成し、列方向に並べます（①②③④⑤⑥ではなく、①④②⑤③⑥の順）。理由は、母音と子音の組み合わせの規則性からプログラムを作ってみると、シンプルかと考えたためです。
・最後に、点字の配列を`6x1`から`2x3`に整形して、戻り値として返しています。
と、いうことをやっています。

```ruby
# 母音と子音を3bit単位で判別するために、点の有無を「① ④ ② ⑤ ③ ⑥ 」の順に列指向で並べている
class TenjiMaker
  def to_tenji(text)
    tenji_serials = text.split(' ').map { |romaji| make_tenji(romaji) }
    [0..1, 2..3, 4..5].map do |range|
      tenji_serials.map { |tenji_serial| tenji_serial[range] }.join(' ')
    end.join("\n")
  end
```

　イメージは、以下です。

　　点字の配列　⇄　プログラム上の一時的な配置
　　　　①④　　　　　　　①④② ⑤③⑥
　　　　②⑤　　　　　　　 母音　子音
　　　　③⑥

　　例：`'A HI RU'`
　　点字の配列　⇄　プログラム上の一時的な配置
　　　A HI RU　　　　　 A　　　　HI 　　　RU
　　　o- o- oo　　　　o-- ---　 o-o -oo　 oo- o--
　　　-- o- -o　　　　　A　 　　 I 　 H　　U　 R
　　　-- oo --
## サブメソッド
サブメソッドは、`make_tenji`と`make_tenji_3bit`の2つです。
・`make_tenji`で、ローマ字の「文字の長さ」と「文字自体」を条件として、ローマ字の種類ごとに分岐させます。
・`make_tenji_3bit`では、`make_tenji`から受け取った2つの引数`target`と`char`で点字情報を生成します。

```ruby
TENJI_3BIT_TABLE = {
  '0' => '---',
  '1' => '--o',
  '2' => '-o-',
  '3' => '-oo',
  '4' => 'o--',
  '5' => 'o-o',
  '6' => 'oo-',
  '7' => 'ooo'
}.freeze

def make_tenji(romaji)
  if romaji.length == 1
    case romaji
    when 'N'     ; '---ooo'
    else         ; "#{make_tenji_3bit('xxxOAIUE', romaji)}---"
    end
  else
    case romaji
    when /^[YW]/ ; "#{make_tenji_3bit('WxYxxxxx', romaji[0])}#{make_tenji_3bit('xxAUxxOx', romaji[1])}"
    else         ; "#{make_tenji_3bit('xxxOAIUE', romaji[1])}#{make_tenji_3bit('xKNHRSTM', romaji[0])}"
    end
  end
end

def make_tenji_3bit(target, char) = (target =~ /#{char}/).to_s.sub(/./, TENJI_3BIT_TABLE)
```

　イメージは、以下です。

　　例1：`'A'`
　　　 母音①④②　'o--'　（`'xxxOAIUE' =~ /A/ #=> 4`　→　`'o--'`）
　　　 子音⑤③⑥　'---'　（子音がないので★直書き）

　　例2：`'HI'`
　　　 母音①④②　'o-o'　（`'xxxOAIUE' =~ /I/ #=> 5`　→　`'o-o'`）
　　　 子音⑤③⑥　'-oo'　（`'xKNHRSTM' =~ /H/ #=> 3`　→　`'-oo'`）

　　例3：`'RU'`
　　　 母音①④②　'oo-'　（`'xxxOAIUE' =~ /U/ #=> 6`　→　`'oo-'`）
　　　 子音⑤③⑥　'o--'　（`'xKNHRSTM' =~ /R/ #=> 4`　→　`'o--'`）

・括弧内は、正規表現を使ったイメージで、`=~`演算子の左辺が検索対象の文字列、右辺が正規表現（スラッシュで囲んだもの）です。
・正規表現では、検索結果がマッチすると、マッチした位置を0（ゼロ）始まりの位置で返します。
・この検索結果がマッチする位置を、ハッシュ定数`TENJI_3BIT_TABLE`の中から、対応する点字の①④②と⑤③⑥の3bit情報（---, --o, ..., ooo）に変換して返しています。
　（母音と子音の組み合わせの規則性から外れる、'A'や'N'などの1文字のローマ字では、★直書きもしています。）

正規表現については、伊藤さんのQiita記事が手を動かしながら学習できるのでオススメです。
（フィヨルドブートキャンプ内のチェリー本輪読会で、その４まで通しで学習して勉強になりました！）
[初心者歓迎！手と目で覚える正規表現入門・その１「さまざまな形式の電話番号を検索しよう」 \- Qiita](https://qiita.com/jnchito/items/893c887fbf19e17d3ff9)
[初心者歓迎！手と目で覚える正規表現入門・その２「微妙な違いを許容しつつ置換しよう」 \- Qiita](https://qiita.com/jnchito/items/64c3fdc53766ac6f2008)
[初心者歓迎！手と目で覚える正規表現入門・その３「空白文字を自由自在に操ろう」 \- Qiita](https://qiita.com/jnchito/items/6f0c885c1c4929092578)
[初心者歓迎！手と目で覚える正規表現入門・その４（最終回）「中級者テクニックをマスターしよう」 \- Qiita](https://qiita.com/jnchito/items/b0839f4f4651c29da408)

# コードのアピールポイント
## 頑張ったところ
１日半、学習のことを忘れて夢中になって作っていたところです。プログラミングの楽しさをあらためて実感しました。

## 苦労したところ
自分の中でロジックが整理できていなかったこともあり、リファクタリングを納得いくところまで、テスト駆動開発（プログラムを壊しては綺麗にしてテストでPass確認するのを繰り返す）していったところです。今回の問題においても、テストが自動化されている環境は開発しやすいことを実感しました。

## 工夫したところ
チェリー本と伊藤さんのQiita記事で正規表現を学習したアウトプットとして、今回、（簡単なものばかりですが、）正規表現を活用したところです。

## 次に試したいこと
伊藤さんの解答例を見て、今回の自分の考え方での問題点を確認していきたいです。

# 伊藤さんにメッセージ
いつも、フィヨルドブートキャンプやチェリー本、Qiita記事などで大変お世話になっております！
今回も、面白い問題をありがとうございました！銀座Railsでの伊藤さんの解答例、楽しみにしております！！

# さいごに
最後までお読みいただき、ありがとうございます。

投稿前は、Qiitaに技術記事を投稿することのハードルの高さを感じていましたが、実際にやってみると、フィヨルドブートキャンプで毎日のように書いている日報（Qiitaと同様のMarkdown形式）と同じ要領で書くことができました。何かを継続して習慣化することの大事さをあらためて感じました。

今回の記事をご覧になって、「まだ、こういう記事を書いたことがないけど、このレベル・分量なら自分にもなんだか書けそうな気がしてきた！」と思っていただければ幸いです。

# 備考
冒頭でも述べましたが、辛口バージョンにもチャレンジしています。
もし、お時間ありましたら、どんなふうに機能を拡張しているかをご覧になられてください。
[辛口バージョン](https://github.com/aim2bpg/tenji-maker-challenge-for-ginza-rails/blob/modiy_tenji_maker_part_2/lib/tenji_maker.rb)