Rubyプログラミング問題にチャレンジ! -改訂版・チェリー本発売記念-のカレンダー | Advent Calendar 2021- Qiita に参加しました。12/7の担当です。
週に一度AtCoder参加してRubyでプログラムを書く練習をしているんですが、難易度的にもボリューム的にもちょうど良さそうだったので参加してみました。
チェリー本やフィヨルドブートキャンプでお世話になっている伊藤さんの企画ということで少しでもお役に立てれば幸いです〜
点字メーカープログラム Advent Calendar 12/7 by obregonia1 · Pull Request #16 · JunichiIto/tenji-maker-challenge
今回のお題は「点字メーカープログラム」ということだったのでまずプログラムの仕様と点字の仕組みについて確認する必要がありました。
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
あらかじめテストコードも作成されていてこのようなコードで動くプログラムを作成する必要があります。
点字の仕組みもこのように母音と子音の点のパターンの組み合わせで表現することがわかりました。
今回の変換対象となる文字は「あいうえお、かきくけこ、(省略)、やゆよ、らりるれろ、わ、ん」のみでしたが、イレギュラーなパターンの文字は「や、ゆ、よ、わ、ん」の5文字だということもわかりました。
まずどうやって実装していくか考え、受け取る文字列は空白区切りになっているので空白で区切ったあとそれぞれの文字を点で表現しました。
縦3点、横2点の点字をどう表現するか迷いましたがシンプルに左上から"Z"の向きに数えて変換することにしました。
あ
o-
--
-- #=> 'o-----'
い
o-
o-
-- #=> 'o-o---'
受け取ったローマ字を点に変換する際にハッシュを作っておいてそれを元に点字に変換しました。
# vowel: 母音
VOWELS = {
A: 'o--',
I: 'o-o',
U: 'oo-',
E: 'ooo',
O: '-oo'
}
# consonant: 子音
CONSONANTS = {
K: '--o',
S: 'o-o',
T: 'oo-',
N: '-o-',
H: '-oo',
M: 'ooo',
Y: '-o-',
R: 'o--'
}
# roman_charには'A', 'KA', 'SA'のようにローマ字一文字が入る
def to_tenji_each_char(roman_char)
if roman_char.size == 1
roman_char == 'N' ? '---ooo' : VOWELS[roman_char.to_sym] + '---'
elsif roman_char.size == 2
consonant, vowel = roman_char.chars
if consonant == 'Y'
CONSONANTS[:Y] +
case vowel
when 'A'
'-o-'
when 'U'
'-oo'
when 'O'
'oo-'
end
elsif roman_char == 'WA'
'----o-'
else
VOWELS[vowel.to_sym] + CONSONANTS[consonant.to_sym]
end
end
end
イレギュラーな「や、ゆ、よ、わ、ん」は愚直に条件分岐してしまったので入れ子になってしまい、少し読みにくくなってしまいました。
点字を横一列で作っているので、点字として出力しやすいように上、中、下の3列に分割しました。
def split_three_row(one_line_tenji_array)
top, middle, bottom = Array.new(3) { [] }
push_to_row = ->(row, tenji, start_point) { row << tenji[start_point, 2] }
one_line_tenji_array.each do |one_line_tenji|
push_to_row.call(top, one_line_tenji, 0)
push_to_row.call(middle, one_line_tenji, 2)
push_to_row.call(bottom, one_line_tenji, 4)
end
return top, middle, bottom
end
空配列を用意してそれぞれに点字を入れていく処理は push_to_row
でLambdaで書いてみました。自分でプログラムを書くときにLambdaを使ったことがなかったので使いたかっただけです笑
あまりコードの短縮にはなってないですが少しだけ処理がわかりやすくなった気がする。。。
あとは文字毎にスペースを入れたものが返り値になればいいので Array#join
とヒアドキュメントを使いました
def to_tenji(text)
roman_chars = text.split(' ')
one_line_tenji_array = roman_chars.map do |roman_char|
to_tenji_each_char(roman_char)
end
top, middle, bottom = split_three_row(one_line_tenji_array)
<<~TENJI.chomp
#{top.join(' ')}
#{middle.join(' ')}
#{bottom.join(' ')}
TENJI
end
コードを書く前は「読みやすくてカッコいいコード書こう!」と思ってましたがやってみるとなかなかそうはいかないですね。まだまだ経験が浅いので色んなコードを書いて自分でも納得できるようなコードが書けるようになりたいです。
最初はとにかく動くコードを書いてみてどんどんリファクタリングしていったんですが、変数名などをわかりやすくして、他の人が読んでも処理の流れがわかりやすいように意識しました。
他に参加している方のコードも読んで勉強したいと思います!