PRへのリンク
ロジック
処理の流れ
大まかな流れは
- テキストを1文字に分解
- 文字を0,1で構成された配列に変換する
- 配列で表された文字を出力しやすいように整形して出力する
となります💡
文字を配列に変換する
文字をバイナリを要素とした配列で表現する
今回のチャレンジに出てくるひらがなは6箇所に●またはーを入れることで表現することができるので、長さ6の配列を使えば表現できそうです。
また「カ行」や「サ行」を見るとわかる通り、「ア行」の①、②、④の部分を残して●を加えて変化させているという規則性が見られます。ここで「加える」ということに注目すると、●とーを1,0で表現し加算することで多くのひらがなを表現できそうです💡
なので、要素は1または0であり長さ6の配列を使って点字を表すことにします。
(例:「あ」は[1,0,0,0,0,0]
)
変換の方法
上記のように
- 母音部分は①②④の点
- 子音部分は③⑤⑥の点
で規則的に表現することができます。(例外を除く)
そこで母音の配列、子音の配列を定数として定義し、配列をベクトル的に加算することで対象の文字を出すことができます。
例えば「し」を配列に変換したい場合、
母音部分のI[1,1,0,0,0,0]
と子音部分のS[0,0,0,0,1,1]
をベクトル的に加算して[1,1,0,0,1,1]
とすることで変換が可能です。
なお、配列をそのまま加算してもベクトル的な加算はできないので工夫が必要です。
# そのまま加算しても後ろに付け足されるだけ
[1,2,3] + [1,2,3]
=> [1, 2, 3, 1, 2, 3]
# ベクトル的に加算
[[1,2,3], [1,2,3]].transpose.map{|a| a.inject(:+) }
=> [2, 4, 6]
参考:https://qiita.com/Kta-M/items/b54f12d3eb49520925e7#comment-e192ccc9e888a565ce4b
変換部分の実装は以下となります💡
VOWELS = { A: [1, 0, 0, 0, 0, 0],
I: [1, 1, 0, 0, 0, 0],
U: [1, 0, 0, 1, 0, 0],
E: [1, 1, 0, 1, 0, 0],
O: [0, 1, 0, 1, 0, 0] }.freeze
CONSONANTS = { K: [0, 0, 0, 0, 0, 1],
S: [0, 0, 0, 0, 1, 1],
T: [0, 0, 1, 0, 1, 0],
N: [0, 0, 1, 0, 0, 0],
H: [0, 0, 1, 0, 0, 1],
M: [0, 0, 1, 0, 1, 1],
R: [0, 0, 0, 0, 1, 0] }.freeze
IRREGULARS = { YA: [0, 0, 1, 1, 0, 0],
YU: [0, 0, 1, 1, 0, 1],
YO: [0, 0, 1, 1, 1, 0],
WA: [0, 0, 1, 0, 0, 0],
N: [0, 0, 1, 0, 1, 1] }.freeze
# 引数は 'KA'などの文字
def letter_to_tenji_binary(letter)
return IRREGULARS[letter.to_sym] if letter.match?(/W|Y|N$/)
consonant = CONSONANTS[letter[0].to_sym] || Array.new(6, 0)
vowel = VOWELS[letter[-1].to_sym]
[consonant, vowel].transpose.map { |a| a.inject(&:+) }.to_a
end
バイナリの配列を点字として出力しやすいように整形する
puts tenji_maker.to_tenji('A HI RU')
#=> o- o- oo
# -- o- -o
# -- oo --
チャレンジでは上記のような出力が求められています。
文字ごとに①と④→改行→②と⑤→改行→③と⑥
と出力すれば、求められている結果を出力できます。
よってN文字を変換する場合は、長さ3の配列で、
- 1番目にはそれぞれの文字の①と④の値の配列がN個
- 2番目にはそれぞれの文字の②と⑤の値の配列がN個
- 3番目にはそれぞれの文字の③と⑥の値の配列がN個
入る配列を作ることにしました。
実際のコードは以下となります。
# 引数にはには①〜⑥の順番で並んだ長さ6の配列が文字数分だけ入る
# 例)"A SA"の場合[[1, 0, 0, 0, 0, 0],[1, 0, 0, 0, 1, 1]]
def tenji_binaries_to_matrix(tenji_binaries)
tenji_binaries.map { |letter|
letter.each_slice(3).to_a.transpose
}.transpose
end
出力部分
ここまできたらあとはそれぞれ1はo、0は-として変換し、出力すれば完成となります💡
# 点字の出力
def matrix_to_tenji(tenji_binaries_matrix)
tenji_binaries_matrix.map do |binary_array|
binary_array_to_tenji_line(binary_array)
end.join("\n")
end
# 一行分の点字を出力する
# [0,1],[1,1] => '-o oo'
def binary_array_to_tenji_line(binary_array)
binary_array.map { |bin|
bin.inject('') { |result, b| result + binary_to_dot(b) }
}.join(' ')
end
def binary_to_dot(binary)
binary.zero? ? '-' : 'o'
end
工夫したところ
YA・YU・YOの規則的な変化を不規則として扱った
上記の通りYA・YU・YOも規則的な変化をしている部分もありますが、たった3文字の規則性のためにロジックを実装するよりも不規則な文字として扱った方が圧倒的に実装の工数が減りそうだったので不規則文字として扱わせていただきました。(良い方法が思いつかなかったという言い訳でもあります。)
定数を外部ファイルに持たせた📁
オープンクラスの機能を使い、定数を外部ファイルに持たせました
class TenjiMaker
VOWELS = { A: [1, 0, 0, 0, 0, 0],
I: [1, 1, 0, 0, 0, 0],
U: [1, 0, 0, 1, 0, 0],
E: [1, 1, 0, 1, 0, 0],
O: [0, 1, 0, 1, 0, 0] }.freeze
CONSONANTS = { K: [0, 0, 0, 0, 0, 1],
S: [0, 0, 0, 0, 1, 1],
T: [0, 0, 1, 0, 1, 0],
N: [0, 0, 1, 0, 0, 0],
H: [0, 0, 1, 0, 0, 1],
M: [0, 0, 1, 0, 1, 1],
R: [0, 0, 0, 0, 1, 0] }.freeze
IRREGULARS = { YA: [0, 0, 1, 1, 0, 0],
YU: [0, 0, 1, 1, 0, 1],
YO: [0, 0, 1, 1, 1, 0],
WA: [0, 0, 1, 0, 0, 0],
N: [0, 0, 1, 0, 1, 1] }.freeze
end
require 'constants'
class TenjiMaker
.
.
伊藤さんへのメッセージ
Railsエンジニアとなって約3ヶ月が経ちますが、チェリー本には今でもお世話になっております🎉
block
の項目などは初見では難しい上に「これ何に使うんだろ?」といった感じでした。しかし実務に入り色々なコードに触れてから改めて読んでみるとすんなりと理解することができ、gemのソースコードを読む時や難しい本を読む時の基礎として非常に役に立っております。ありがとうございました🍒🎸