はじめに
前回に引き続き、Ruby学習のアウトプットとして今回はゴルフのスコア名を出力するプログラムを作成しました。
上記の内容について、備忘のためにメモ書きを残しておくことにします。
アウトプットイメージ
以下のようなテキストファイル(1行目:規定打数、2行目:実際の打数)を入力値として、ゴルフのスコア名を出力します。
入力値のそれぞれの値の範囲は以下の通りです。
3 ≤ 規定打数 ≤ 5
1 ≤ 実際の打数
※ ゴルフのルールやスコア名の説明は省略させていただきます。
4,4,5,3,5,4,4,3,4,4,5,4,4,3,4,4,3,5
2,3,1,5,8,3,5,1,5,6,2,5,7,2,5,5,2,6
出力の例
イーグル,バーディ,コンドル,2ボギー,3ボギー,バーディ,ボギー,ホールインワン,ボギー,2ボギー,アルバトロス,ボギー,3ボギー,バーディ,ボギー,ボギー,バーディ,ボギー
なお、今回のプログラムは下記のコマンドで実行します。
具体的には、cat
コマンドでファイルの中身を標準出力しパイプでつなげ、Rubyプログラムで標準出力を受け取るようにします。
cat case_1.txt | ruby golf_score.rb
作業手順
今回は、以下の2ステップでプログラムを作成していきます。
- スコア名を出力する関数の作成
- 入力値を関数の引数に渡す
また、事前に以下のようなゴルフの規定打数とスコア名の対応表を作成し整理しておきます。
実際の打数 | 規定打数3 | 規定打数4 | 規定打数5 |
---|---|---|---|
1 | ホールインワン(-2) | ホールインワン(-3) | コンドル(-4) |
2 | バーディ(-1) | イーグル(-2) | アルバトロス(-3) |
3 | パー(0) | バーディ(-1) | イーグル(-2) |
4 | ボギー(+1) | パー(0) | バーディ(-1) |
5 | 2ボギー(+2) | ボギー(+1) | パー(0) |
6 | 3ボギー(+3) | 2ボギー(+2) | ボギー(+1) |
7 | +4 | 3ボギー(+3) | 2ボギー(+2) |
8 | +5 | ダブルパー(+4) | 3ボギー(+3) |
9 | +6 | +5 | ダブルスコア(+4) |
10 | +7 | +6 | ダブルパー(+5) |
スコア名を出力する関数の作成
スコア名を出力するには、スコアが必要です。
スコアは以下の式で求められ、先ほどの表の()の中の値にあたります。
スコア = 実際の打数 - 規定打数
ただ、次のように「実際の打数」が同じ1打でも規定打数によってスコア名の呼び名が異なるパターンがあるため、考慮が必要です。
- 規定打数4:ホールインワン
- 規定打数5:コンドル
ひとまずコードを書いてみたのですが、冗長かつ、case
文のネストが深くなっています。リファクタリングしていくことにします。
def show_golf_score_name(par, score)
golf_score = score - par
case par
when 5
case golf_score
when -4
print 'コンドル'
when -3
print 'アルバトロス'
when -2
print 'イーグル'
when -1
print 'バーディ'
when -0
print 'パー'
when 1
print 'ボギー'
when 2
print '2ボギー'
when 3
print '3ボギー'
when 4
print 'ダブルスコア'
when 5
print 'ダブルパー'
end
when 4
case golf_score
when -3
print 'ホールインワン'
when -2
print 'イーグル'
when -1
print 'バーディ'
when -0
print 'パー'
when 1
print 'ボギー'
when 2
print '2ボギー'
when 3
print '3ボギー'
when 4
print 'ダブルパー'
when 5
print '+5'
when 6
print '+6'
end
when 3
case golf_score
when -2
print 'ホールインワン'
when -1
print 'バーディ'
when -0
print 'パー'
when 1
print 'ボギー'
when 2
print '2ボギー'
when 3
print '3ボギー'
when 4
print '+4'
when 5
print '+5'
when 6
print '+6'
when 7
print '+7'
end
end
end
show_golf_score_name(3, 1)
show_golf_score_name(4, 3)
show_golf_score_name(5, 10)
スコアが「ほげほげ」の時には、スコア名「ふがふが」を出力するという関数なのでキーと値の関係になっています。
上記のような場合には、ハッシュが利用できそうです。
def show_golf_score_name(par, score)
par_5 = { -4 => 'コンドル', -3 => 'アルバトロス', -2 => 'イーグル', -1 => 'バーディ', -0 => 'パー', 1 => 'ボギー', 2 => '2ボギー', 3 => '3ボギー', 4 => 'ダブルスコア', 5 => 'ダブルパー' }
par_4 = { -3 => 'ホールインワン', -2 => 'イーグル', -1 => 'バーディ', -0 => 'パー', 1 => 'ボギー', 2 => '2ボギー', 3 => '3ボギー', 4 => 'ダブルパー', 5 => '+5', 6 => '+6' }
par_3 = { -2 => 'ホールインワン', -1 => 'バーディ', -0 => 'パー', 1 => 'ボギー', 2 => '2ボギー', 3 => '3ボギー', 4 => '+4', 5 => '+5', 6 => '+6', 7 => '+7' }
golf_score = score - par
case par
when 5
puts par_5.values_at(golf_score) if par_5.has_key?(golf_score)
when 4
puts par_4.values_at(golf_score) if par_4.has_key?(golf_score)
when 3
puts par_3.values_at(golf_score) if par_3.has_key?(golf_score)
end
end
show_golf_score_name(3, 1)
show_golf_score_name(4, 3)
show_golf_score_name(5, 10)
コードが短くなりましたが、まだリファクタリングの余地がありそうです。最後に以下の2点を意識して、リファクタリングを進めていきます。
- 共通の内容の値が入るハッシュは、1つのハッシュにまとめる
- DRY原則に従って、似たような処理(重複)を避ける
def show_golf_score_name(par, score)
score_names = {
5 => { -4 => 'コンドル', -3 => 'アルバトロス', -2 => 'イーグル', -1 => 'バーディ', -0 => 'パー', 1 => 'ボギー', 2 => '2ボギー', 3 => '3ボギー', 4 => 'ダブルスコア', 5 => 'ダブルパー' },
4 => { -3 => 'ホールインワン', -2 => 'イーグル', -1 => 'バーディ', -0 => 'パー', 1 => 'ボギー', 2 => '2ボギー', 3 => '3ボギー', 4 => 'ダブルパー', 5 => '+5', 6 => '+6' },
3 => { -2 => 'ホールインワン', -1 => 'バーディ', -0 => 'パー', 1 => 'ボギー', 2 => '2ボギー', 3 => '3ボギー', 4 => '+4', 5 => '+5',6 => '+6', 7 => '+7' }
}
golf_score = score - par
if score_names[par] && score_names[par].has_key?(golf_score)
print score_names[par][golf_score]
end
end
show_golf_score_name(3, 1)
show_golf_score_name(4, 3)
show_golf_score_name(5, 10)
最初のコードよりもかなり短くなり、可読性も向上しました。
入力値を関数の引数に渡す
次に、テキストファイルの値をshow_golf_score_name
関数の引数に渡していきます。
関数の第一引数にテキストファイルの1行目の値を、第二引数に2行目の値をペア(列ごと)で渡し、繰り返し処理で順番に関数実行していきます。
今回は、gets
メソッドを利用します。
4,4,5,3,5,4,4,3,4,4,5,4,4,3,4,4,3,5
2,3,1,5,8,3,5,1,5,6,2,5,7,2,5,5,2,6
まずは、空の配列にgets
メソッドでテキストファイルの内容を行単位で2行(2回)追加します。
なお、chomp
メソッドがないと不要な改行文字が入ってしまいます。
lines = []
2.times do
lines << gets.chomp
end
配列に以下のような形で値が追加されているのが分かります。
あとは、配列の値を関数の引数に渡す処理を書いて完成となります。
lines = []
2.times do
lines << gets
end
par_arr = lines[0].split(',')
score_arr = lines[1].split(',')
result_scores = []
par_arr.zip(score_arr) do |par_value, score_value|
result_scores << show_golf_score_name(par_value.to_i, score_value.to_i)
end
print result_scores.join(',')
テキストファイルの入力値からスコア名を出力することができました。
おわりに
あまりリファクタリングの経験がない私ですが、リファクタリング時には作成したコードのどの箇所が問題か明確にすることが重要だと感じました。
引き続きたくさん失敗をして、経験を積んでいきます。