Ruby

GPAを計算するプログラムを書こうとした話

自分の在学している某大学で先日成績開示がありました。講義をあまりよく聞いておらずテストの数日前に火の車のごとく猛勉強(という名のゴリ押し)をしてなんとか無事単位の味を噛みしめることができました。

それはそうとして、気になるのは成績評価の指標であるGPAです。いちいち電卓打つのも面倒だしアプリを入れるのもネットを使うのもなあ。というわけで今まで勉強してきたショボい知識をフル活用してGPAを計算してくれるプログラムを作ろう!と言う流れになりました。PC開いてプログラム実行するほうがよほど面倒だろ

GPAってなによ

自分で調べてください。

1作目

とりあえずコードを見てください。

gpa-calc.rb
puts "履修単位数を入力してください"
ln = gets.to_i

puts "GPA計算を行います。"

i = 1.0 #単位数1.0の科目からスタート
sum = 0

while i < 4.0 #1教科ごとの単位数の最大が(某大学では)3.0
   puts "単位数#{i}の教科について計算します。"

   puts "秀の科目数"
   s = gets.to_i

   puts "優の科目数"
   a = gets.to_i

   puts "良の科目数"
   b = gets.to_i

   puts "可の科目数"
   c = gets.to_i

   puts "不可の科目数"
   d = gets.to_i

   sum = sum + (s*4 + a*3 + b*2 + c)*i
   i = i + 1.0
end

gpa = sum / ln

puts "あなたのGPAは%.2fです。" % gpa

単位数1.0の科目から順に各成績ごとに個数を打ち込んで計算する力押し方です。ぶっちゃけCで組める。Rubyっぽい要素がそんなにない。実行するとこんな感じ。

履修単位数を入力してください
22
GPA計算を行います。
単位数1.0の教科について計算します。
秀の科目数
0
優の科目数
1
良の科目数
1
可の科目数
1
不可の科目数
0
単位数2.0の教科について計算します。
秀の科目数
1
優の科目数
3
良の科目数
2
可の科目数
1
不可の科目数
1
単位数3.0の教科について計算します。
秀の科目数
0
優の科目数
0
良の科目数
1
可の科目数
0
不可の科目数
0
あなたのGPAは2.18です。

成績はサンプルで適当に組みました。自分のではないです。見にくいですがちゃんと実行できていますね。どことない「C言語でいいよな」感。

2作目

Ruby感を出したい!な2作目。

gpa-calc.rb
puts "履修単位数を入力してください"
ln = gets.to_i

puts "GPA計算を行います。"
puts "秀 優 良 可 不可  の順番に取得した単位数を入力。"

a = 0
sum = 0
i = 1.0
suml = 0

while i < 4.0
  puts "単位数#{i}の科目について計算します。"

  s, a, b, c, d = gets.split(" ").map!{|n| n.to_i} #Rubyっぽい

  sum = sum + (s*4 + a*3 + b*2 + c) * i
  suml = suml + (s+a+b+c+d) * i.to_i #今までの合計
  i = i + 1.0
end

if suml > ln
  puts "入力した単位数が履修単位数を超えています。"
else
  gpa = sum / ln
  puts "あなたのGPAは%.2fです。" % gpa
end

説明するより出力見たほうが早いと思います。

履修単位数を入力してください
22
GPA計算を行います。
秀 優 良 可 不可  の順番に取得した単位数を入力。

単位数1.0の科目について計算します。
0 1 1 1 0

単位数2.0の科目について計算します。
1 3 2 1 1 

単位数3.0の科目について計算します。
0 0 1 0 0
あなたのGPAは2.18です。

出力にある通り、各単位数の科目で、左から順に秀・優・良・可・不可の個数を半角スペース区切りで入力するとGPAが出ます。

while内2行目の説明です。1作目と同じく秀・優・良・可・不可のそれぞれに変数を割り当てる必要がありますが、わざわざ5個puts置くのもなあと感じたので1行で5個一気に入力してしまいます。

s, a, b, c, d = gets

ただこれだけだと、何を入力してもそれ全体がString型の文字列になってsに格納されてしまい、a以降には何も入りません。

検証と出力

main.rb
s, a, b, c, d = gets #3 0 2 4 0   と入力
p s
p s.class
p a
p a.class
3 0 2 4 0
"3 0 2 4 0\n"
String
nil 
NilClass

aがnilになっていて、オブジェクトがないことがわかります。そしたら次に、入力された文字列を特定の文字に従って分割して配列に格納して返すString#splitを使います。今回は半角スペースを使ったのですがまあ何でもいいです。すると...

main.rb
s, a, b, c, d = gets.split(" ") #3 0 2 4 0   と入力
p s
p s.class
p a
p a.class
3 0 2 4 0
"3"
String
"0"
String

あらかじめ変数を複数設定してあるので、1つの変数ごとに配列の要素が1個ずつ、String型で入っていることがわかります。なお、変数の指定を1つだけにすると、その変数にArray型の配列そのものが返されます。あとは、String型をto_iメソッドで変換するだけです。変換も一気にやっちゃおうということで、配列の各要素を順番にブロックに渡して評価し、その結果で要素を置き換えるArray#map!を使います。要素そのものを変えたいので破壊的メソッドです。これで、全部の要素を変換出来ました。

main.rb
s, a, b, c, d = gets.split(" ").map!{|n| n.to_i}
p s
p s.class
p a
p a.class
3 0 2 4 0
3
Integer
0
Integer

あとは、オプションとして打ち間違いか何かで最初に入力した履修単位数より多くなってしまったらエラーになります。他の例外処理やエラーも時間があったら付け足したい。

終わりに

プログラムをはいよっと渡すだけにしたかったのに長々と説明してしまいました。稚拙なプログラムですが、もし需要があったら自由に使ってもらって構いません。また、加筆修正や間違いの指摘も受け付けてます。最後まで読んでいただきありがとうございました。