配列を利用して、試験の得点の処理をするプログラムを作成してみよう。 国語の試験の結果ファイルとして以下のようなデータを作成しよう。
山田太郎,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)まですべての整数の集合を表す。
以上を組み合わせてプログラムは完成する。
#!/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
# 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