本格的なデータ処理

配列、ハッシュに関してここまでに覚えたことを利用すると、 個人で処理する規模であればどんなデータでもRubyプログラムで 処理することができる。

成績処理の例

以下のような成績データがある。

score2.txt

山田太郎        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+)→数学の得点が入る
    point[$1] = [$2.to_i, $3.to_i] # 配列を代入
  end
end

この結果の一覧を、国語の得点の高い順に 出力しよう。

まず、全てのデータを読み込んだ状態では、point変数の 値は以下のようなハッシュになっている。

point = {
  "山田太郎" => [50, 40],
  "中町太郎" => [90, 70],
  "飯森花子" => [91, 90],
  "鶴岡一人" => [60, 50],
  "酒田三吉" => [52, 80],
  "三川一二三" => [12, 75]
  }

これを国語の点の高い順に並べ換えたい。並べ換えるにはまず 各ハッシュの要素の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

これを組み合わせてプログラムは完成する。

kokugo-rank.rb

#!/usr/koeki/bin/ruby
# coding: utf-8
defined?(Encoding) && Encoding.default_external = "binary"

point = Hash.new

while yline = gets
  if /(\S+)\s+(\d+)\s+(\d+)/ =~ yline
    # 1個目の() (\S+)→氏名が入る
    # 2個目の() (\d+)→国語の得点が入る
    # 3個目の() (\d+)→数学の得点が入る
    point[$1] = [$2.to_i, $3.to_i] # 配列を代入
  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
------------------------------------

問題

  1. 数学の点の高い順に結果を出力するプログラムを作り 実際に実行せよ。

  2. 一覧表示に、数学と国語の合計点も追加出力するようにし、 なおかつ合計点の高い順に結果を出力するプログラムを作り 実際に実行せよ。


本日の目次