配列、ハッシュに関してここまでに覚えたことを利用すると、 個人で処理する規模であればどんなデータでもRubyプログラムで 処理することができる。
以下のような成績データがある。
山田太郎 50 40 中町太郎 90 70 飯森花子 91 90 鶴岡一人 60 50 酒田三吉 52 80 三川一二三 12 75
各行の並びは「氏名、国語の得点、数学の得点」だと仮定する。 まず、単純な例として、これを全て読み込み、国語の得点の高い順に並べ換えた ものを出力するようにしよう。
データを読み込む部分までは前回と同じである。point
という変数に、
{ 氏名 => [国語の点, 数学の点],... }
というハッシュ値を格納するプログラムは以下のようになる。
point = Hash.new while yline = gets if /(\S+)\s+(\d+)\s+(\d+)/ =~ yline # 1個目の() (\S+)→氏名が入る # 2個目の() (\d+)→国語の得点が入る # 3個目の() (\d+)→数学の得点が入る j = $2.to_i # 国語の得点 m = $3.to_i # 数学の得点 point[$1] = [j, m, j+m] # [国語得点, 数学得点, 合計] の配列を代入 end end
この結果の一覧を、国語の得点の高い順に 出力しよう。
まず、全てのデータを読み込んだ状態では、point
変数の
値は以下のようなハッシュになっている。
point = { "山田太郎" => [50, 40, 90], "中町太郎" => [90, 70, 160], "飯森花子" => [91, 90, 181], "鶴岡一人" => [60, 50, 110], "酒田三吉" => [52, 80, 132], "三川一二三" => [12, 75, 87] }
これを国語の点の高い順に並べ換えたい。並べ換えるにはまず
各ハッシュの要素のkeyだけを取り出した上で、sort
メソッドを使
う。ただし、keyの値(ここでは氏名)で並べ換えても意味がないので、
sort
にブロックを指定して、国語の点どうしで比較するようにす
る。それには以下のようにする。
point.keys.sort {|x, y| point[x][0] <=> point[y][0]}
変数 x, y
には、比較対象となる氏名が入る。
つまり、point[x]
は、選ばれた誰か(xさん)の得点の配列
(もし 山田太郎なら [50, 40]
)が得られることになる。
この配列の第0要素が国語の点なので、
point[x][0]
が「xさん」の国語の得点ということになる。続いて、
point.keys.sort {|x, y| point[x][0] <=> point[y][0]}
を評価すると、国語の点の「小さい順」に並べ換えた「氏名」が 得られるので結果は
["三川一二三", "山田太郎", "酒田三吉", "鶴岡一人", "中町太郎", "飯森花子"]
となる、これを逆順にすると「国語の得点の高い順」になる。
point.keys.sort {|x, y| point[x][0] <=> point[y][0]}.reverse
↓
["飯森花子", "中町太郎", "鶴岡一人", "酒田三吉", "山田太郎", "三川一二三"]
このキーの順番で結果を出力すればよい。
print "--氏名--------------+-国語-+-数学---\n"
for student in
point.keys.sort {
|x, y| point[x][0] <=> point[y][0]
}.reverse
# student には、学生の氏名が入っている
kokugo = point[student][0]
math = point[student][1]
printf("%-20s %5d %5d\n", student, kokugo, math)
end
puts "-"*36
これを組み合わせてプログラムは完成する。
#!/usr/koeki/bin/ruby # coding: utf-8 require "./kprintf.rb" point = Hash.new while yline = gets if /(\S+)\s+(\d+)\s+(\d+)/ =~ yline # 1個目の() (\S+)→氏名が入る # 2個目の() (\d+)→国語の得点が入る # 3個目の() (\d+)→数学の得点が入る j = $2.to_i # 国語の得点 m = $3.to_i # 数学の得点 point[$1] = [j, m, j+m] # [国語得点, 数学得点, 合計] の配列を代入 end end print "--氏名--------------+-国語-+-数学---\n" for student in point.keys.sort {|x, y| point[x][0] <=> point[y][0] }.reverse # student には、学生の氏名が入っている kokugo = point[student][0] math = point[student][1] printf("%-20s %5d %5d\n", student, kokugo, math) end puts "-"*36
実行結果は以下のようになる。
% ./kokugo-rank.rb score2.txt
--氏名--------------+-国語-+-数学---
飯森花子 91 90
中町太郎 90 70
鶴岡一人 60 50
酒田三吉 52 80
山田太郎 50 40
三川一二三 12 75
------------------------------------
問題
数学の点の高い順に結果を出力するプログラムを作り 実際に実行せよ。
一覧表示に、数学と国語の合計点も追加出力するようにし、 なおかつ合計点の高い順に結果を出力するプログラムを作り 実際に実行せよ。