前回の記事の問題を解いている過程で、"連続した数字を取り出さなければ"と思い、凝った正規表現を作ってしまったので、それを使える問題を作成しました。
★前回の記事
こちらの記事のガラケー文字入力問題の応用編を作成しました。
問題
英語のガラケーでは「2」キーを2回押すと「b」になり、「3」キーを3回押すと「f」になります。
文字を確定するには、'0'または別の数字を入れることで確定されます。
たとえば、このプログラムに対して"443355505556660"を入力すると、"hello"が返ってきます。
解答
class FeaturePhone BUTTONS = { '1' => ['.', ',', '!', '?', ' '], '2' => %w[a b c], '3' => %w[d e f], '4' => %w[g h i], '5' => %w[j k l], '6' => %w[m n o], '7' => %w[p q r s], '8' => %w[t u v], '9' => %w[w x y z] }.freeze def digits_to_alphabet(digits_line) digits_line.scan(/(([1-9])\2*)/).map {|digits, | button_values = BUTTONS[digits[0]] button_values[(digits.length - 1) % button_values.length] }.join end end
テストコードです。
require 'rspec' require File.expand_path(File.dirname(__FILE__) + '/../hello/feature_phone') describe FeaturePhone do describe '#digits_to_alphabet_non_zero' do subject { phone.digits_to_alphabet(digits) } let(:phone){ FeaturePhone.new } context '2のボタンが1回押された場合' do let(:digits) { '20' } it { is_expected.to eq 'a'} end context '4, 3, 5, 6のボタンが2回か3回押され、毎回0で確定された場合' do let(:digits) { '440330555055506660' } it { is_expected.to eq 'hello'} end context '1, 4, 3, 5, 6, 7, 9のボタンが押され、途中に0がない場合' do let(:digits) { '4433555055566611011111090666077755531110' } it { is_expected.to eq 'hello, world!'} end context '0が複数回登場し、5のボタンが8回押されたケースが含まれる場合' do let(:digits) { '000555555550000330000444000080000200004440000' } it { is_expected.to eq 'keitai'} end context '0が複数回登場し、5のボタンが8回押されたケースが含まれる場合' do let(:digits) { '0005555555533444824440000' } it { is_expected.to eq 'keitai'} end end end
解説
1から9の数字で同じ数字が連続している部分を抜き出します。[1-9]+
では、443355
などにマッチしてしまい、同じ数字の連続を取り出すことができません。
同じ数字の連続を取り出すのには、後方参照を使用します。後方参照は、カッコでキャプチャした部分を、\1
の形式で正規表現中で参照できる機能です。同じ数字が2回連続で出現する正規表現は([0-9])\1
で表すことができます。今回は、1から9の数字の1回以上の連続を取り出したいので、正規表現は([1-9])\1*
となります。
String.scan
(
scan (String) - Rubyリファレンス
)で正規表現にマッチする部部分を繰り返し取得します。
ここまでをコードにしていきます。
class FeaturePhone def digits_to_alphabet(digits_line) digits_line.scan(/([1-9])\1*/) end end
入力が0005555555533444824440000
の場合の実行結果です。連続した部分の先頭の文字列が取得できました。これはカッコでキャプチャした部分です。
[["5"], ["3"], ["4"], ["8"], ["2"], ["4"]]
マッチする部分全体を取得するため、キャプチャのカッコを追加します。後方参照の\1
を\2
に変更します。
class FeaturePhone def digits_to_alphabet(digits_line) digits_line.scan(/(([1-9])\2*)/) end end
入力が0005555555533444824440000
の場合の実行結果です。連続した数字が取得できました。
[["55555555", "5"], ["33", "3"], ["444", "4"], ["8", "8"], ["2", "2"], ["444", "4"]]
連続した数字列と最初のの数字の2つをmap
に渡します。
多重代入を使用して、配列の1番目と2番目をそれぞれ別の変数に代入します。
class FeaturePhone BUTTONS = { '1' => ['.', ',', '!', '?', ' '], '2' => %w[a b c], '3' => %w[d e f], '4' => %w[g h i], '5' => %w[j k l], '6' => %w[m n o], '7' => %w[p q r s], '8' => %w[t u v], '9' => %w[w x y z] }.freeze def digits_to_alphabet(digits_line) digits_line.scan(/(([1-9])\2*)/).map {|digits, digit| button_values = BUTTONS[digit] button_values[(digits.length - 1) % button_values.length] }.join end end
そのほかの部分は前回の記事と一緒ですので、参考にどうぞ。
出題者の伊藤淳一さんの書籍はこちら↓