成績処理の例

配列を利用して、試験の得点の処理をするプログラムを作成してみよう。 国語の試験の結果ファイルとして以下のようなデータを作成しよう。

score.csv

山田太郎,50
中町太郎,90
飯森花子,91
鶴岡一人,60
酒田三吉,52
三川一二三,12

このデータを読み込み、全員の平均点を計算したうえで、個人個人の 平均点との得点差も合わせて出力するようなプログラムを作ってみよう。

平均点を計算するためには、まず全員のデータを読み込んで、得点と人数を 求める必要がある。さらに、個人個人の平均点との差を出力するためには、全員 の得点を記憶しておく必要があるので、配列に、 名前・得点を入れておかなければならない。

まず最初に、データファイルを全部読み、名前と得点を配列に記憶しよう。 CSVデータを読む部分は次のように書ける。

seiseki = CSV.read("score.csv")

ただし、この書き方だとデータファイル名を score.csv 以外にできないので、プログラム起動時に自由に指定できるようにする。 このためにARGV配列を用いて、CSV.readでは、

seiseki = CSV.read(ARGV[0])

としておき、ターミナルから実行するときにコマンドライン引数として指定する。

./プログラム名.rb score.csv

のようにすると、プログラム名の直後に書いた単語が ARGV[0] に入る。

score.csvファイルをCSV.readで読み込むと、
1行目のレコードは ["山田太郎", "50"],
2行目のレコードは ["中町太郎", "90"], ……
のように配列化され、さらにそれが配列化されたものが seiseki 変数に入る。

続いて、名前用、得点用2つの配列を用意するとともに 合計得点を記録する変数を加えるようにした上で、 これをRubyプログラムに直すと以下のようになる。

name  = []
score = []
sum = 0				# 合計点計算用
seiseki.each do |row|		# 1レコードずつ取り出したものが順次rowに入る
  # rowには ["山田太郎", "50"] のような形式が順番に入る
  name  << row[0]		# 第0要素は氏名
  score << row[1].to_i		# 第1要素は得点の文字列なので .to_i で整数化
  sum += row[1].to_i
end

これで変数 sum に合計点が入り、 配列の length メソッドで要素数、すなわち 人数が分かるので平均点が計算できる。

average = sum.to_f/score.length		# name.lengthで割ってもよい

割り算をする場合は整数をto_fメソッドで 浮動小数点数に変換することを忘れないようにしよう。

最後に、全員の、氏名、得点、平均点との差、をループで出力する。

i = 0
print "--氏名--------------+-得点-+-平均との差--\n"
while i < score.length
  printf("%-20s %5d   %5.1f\n", name[i], score[i], score[i]-average)
  i += 1
end

添字変数 i を自動的に増やす以下の方法もある。

print "--氏名--------------+-得点-+-平均との差--\n"
for i in 0..(score.length-1)		# 0 から(要素数-1)まで繰り返す
  printf("%-20s %5d   %5.1f\n", name[i], score[i], score[i]-average)
end

N..M は、2つの自然数 N から M (N<M)まですべての整数の集合を表す。

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

score.rb

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

require 'csv'			# CSVライブラリを呼び出す
require './kprintf.rb'		# printfの%Xsでの幅を合わせるため

score=[]
name =[]
sum = 0
seiseki = CSV.read(ARGV[0])	# ./score.rb score.csv のように指定する想定
seiseki.each do |row|		# 1レコードずつ取り出してrowに入れて繰り返す
  # rowには ["山田太郎", "50"] のような形式が順番に入る
  name  << row[0]		# 第0要素は氏名
  score << row[1].to_i		# 第1要素は得点の文字列なので .to_i で整数化
  sum += row[1].to_i
end
average = sum.to_f/score.length

i = 0
print "--氏名--------------+-得点-+-平均との差--\n"
while i < score.length
  printf("%-20s %5d   %5.1f\n", name[i], score[i], score[i]-average)
  i += 1
end

kprintf.rb

# kanji-aware printf for both Ruby1.8 or 1.9+
class String
  require 'kconv'
  if defined?("".force_encoding)
    def toeucbin()
      self.toeuc.force_encoding("binary")
    end
  else
    def toeucbin()
      self.toeuc
    end
  end
end

class IO
  def printf(*args)
    out = sprintf(*(args.collect{|x| x.is_a?(String) ? x.toeucbin : x }))
    print out.toutf8
  end
end

class Object
  def printf(*args)
    if args[0].is_a?(String)
      $stdout.printf(*args)
    else
      port = args.shift
      port.printf(*args)
    end
  end
end

上記プログラムで利用している kprintf.rb を同じディレクトリに保存してから実行してみよう。

./score.rb score.csv
--氏名--------------+-得点-+-平均との差--
山田太郎                50    -9.2
中町太郎                90    30.8
飯森花子                91    31.8
鶴岡一人                60     0.8
酒田三吉                52    -7.2
三川一二三              12   -47.2

本日の目次