問題を解いてRubyの知識を深ぼる。⑦

# はじめに
毎日行ってきたRubyの問題を解くアウトプットは今日で最後になります。

# Q31 条件演算子
3桁の正の整数を入力。その整数の「百の位・十の位・一の位の和」について、
10の倍数（0,10,20,30...）からの差が
- 2以内であるときは"True"
- それ以外は"10の倍数との差は○です"
と表示されるようにする。
出力例
near_ten(117)→True
near_ten(123)→10の倍数との差は4です
near_ten(111)→10の倍数との差は3です
0も10の倍数に含むものする。
百の位・十の位・一の位の和が6などの時に、「10の倍数との差は6です」と出力せずに、「10の倍数との差は4です」と10の倍数から近い方の差を出力する。
解答
```
def near_ten(num)
  total = (num/100) + (num/10 % 10) + (num % 10)
  remainder = total % 10
  if remainder <= 2 || remainder >= 8
    puts "True"
  elsif remainder <= 5
    puts "10の倍数との差は#{remainder}です"
  else 
    puts "10の倍数との差は#{10 - remainder}です"
  end
end
```
ポイント
`num/100（百の位）`
2桁の整数を100で割った値が百の位。
num = 117の場合、num/100 つまり117/100は1.17となる。
Rubyでは整数同士（integer型）の計算だと返り値は整数になるので、小数点以下は切り捨てられて「1」になる。
そのため、百の位の取得は、3桁の数字を100で割るだけで可能となる。
よって、百の位は「1」。
`num/10 % 10（十の位）`
2桁の整数を10で割り、その値を10で割った際の余りが十の位。
num = 117の場合、117/10は11.7になる。整数同士は小数点以下の計算は行わないので、11。
11 % 10 は、11を10で割った際の余りを計算結果として返す。百の位の計算と同様に、小数点以下の計算は行わないので、余りは1となる。
よって、十の位は「1」となる。
`num % 10の場合（一の位）`
2桁の整数を10で割った際の余りが一の位になる。
num = 117の場合、117 % 10 は、117を10で割った際の余りを計算結果として返す。百の位の計算と同様に、小数点以下の計算は行わないので、余りは7。
よって、一の位は「7」。

8行目のelseのポイントは、「百の位・十の位・一の位の和が6または7に該当する場合は、10から差し引いて、10の倍数との差分を導出する必要がある」という点です。

# Q32 任意の文字列から特定の連続する文字列を検知するプログラムの実装
任意の文字列から特定の連続する文字列を探し、その直前の値により「True」または「False」を出力するプログラムを実装。
以下の要件を満たすxyz_thereメソッドを実装。
- 任意の文字列から連続する文字列"xyz"を探し、その直前にピリオド（.）がない場合はTrueを出力する
- 任意の文字列から連続する文字列"xyz"を探し、その直前にピリオド（.）がある場合はFalseを出力する
- 上記2つの条件に当てはまらない場合はFalseを出力する
雛形
```
def xyz_there(str)
  # 処理を記述
end

# 呼び出し例
xyz_there('abcxyz')
```
出力例
xyz_there('abcxyz') → True
xyz_there('abc.xyz') → False
xyz_there('xyz.abc') → True
xyz_there('azbycx') → False
xyz_there('a.zbycx') → False
```
def xyz_there(str) 
  if str.include?(".xyz")
    puts "False"
  elsif str.include?("xyz")
    puts "True"
  else
    puts "False"
  end
end

# 呼び出し例
xyz_there('abcxyz')
```
今までの問題を解ければ解ける問題なので説明は省略。

# Q33 閏年問題
西暦の年数および月を入力し、その月の日数を求めるプログラムを書く。
その場合、閏年について考慮する必要がある。

閏年は以下の判断基準で決まる。

①その西暦が4で割り切れたら閏年である
②ただし、例外として100で割り切れる西暦の場合は閏年ではない
③ただし、その例外として400で割り切れる場合は閏年である

つまり、西暦2000年は閏年であり、西暦2100年は閏年ではない。
これらに対応できるように、出力例と雛形をもとに実装。

出力例
1990年2月 =>"1990年2月は28日間あります"
2000年2月 =>"2000年2月は29日間あります"
2100年2月 =>"2100年2月は28日間あります"
2000年3月=>"2000年3月は31日間あります"
雛形
```
def get_days(year, month)
  month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
  # ここに処理を書き加えてください
end

puts "年を入力してください："
year = gets.to_i
puts "月を入力してください："
month = gets.to_i

days = get_days(year, month)
puts "#{year}年#{month}月は#{days}日間あります"
```
解答
```
def get_days(year, month)
  month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] # 各月の日数は配列で管
  if month == 2 # 2月のとき
    # 閏年のときはdaysに29を代入
    # それ以外はdaysに28を代入
    if year % 4 == 0 # 年が4で割り切れること
      if year % 100 == 0 && year % 400 != 0 # 年が100で割り切れて400で割り切れない場合
        days = 28 # 閏年でない
      else
        days = 29 # 閏年
      end
    else
      days = 28 # 閏年でない
    end
  else
    days = month_days[month - 1]
  end

  return days
end

puts "年を入力してください："
year = gets.to_i
puts "月を入力してください："
month = gets.to_i

days = get_days(year, month)
puts "#{year}年#{month}月は#{days}日間あります"
```

# Q34  if,else問題
メソッドに3つの整数a b cを与える。
・「aとbの差が1」または「aとcの差が1」であり、かつ「bとcとの数値の差が2以上」の場合はTrue
・それ以外はFalse
と出力するメソッドを作成。
出力例：
close_far(1, 2, 10) → True
close_far(1, 2, 3) → False
close_far(4, 1, 3) → True
解答
```
def close_far(a,b,c)
  x = (a-b).abs
  y = (a-c).abs
  z = (b-c).abs

  if (x == 1 && z >= 2) || (y == 1 && z >= 2)
    puts "True"
  else
    puts "False"
  end
end
```
ポイントは`abs`
対象となる数値に対して「abs」メソッドを実行すると絶対値を取得することができる。すなわち正の数の場合はそのままだが負の数の場合は符号を取って正の数にした数値が取得できる。
```
num = 5.abs
#=> 5

num = (-5).abs
#=> 5
```

# Q35 成績管理アプリケーションの作成
以下の仕様を満たすアプリケーションを作成。
仕様
- 実行すると [1] : 点数を登録する, [2] : 点数を確認する, [3] : 終了する という選択肢を表示し、ユーザーに入力を求め、その入力に従い以下のような各処理を行う
- アプリケーションを終了するまで、処理を繰り返す

[1]の処理
・名前、年齢、国語・数学・英語の3教科の点数を入力させて、保存する

[2]の処理
・投稿された情報から番号と名前で一覧を表示し（例 1: yamada）、見たい個人の番号の入力を求める
・ 入力された個人の名前、年齢、国語・数学・英語の3教科の点数を表示する

[3]の処理
・アプリケーションを終了する

雛形
```
def registration_student
  # 生徒の名前と年齢を登録できるようにする
  student = {}
  puts '生徒名を入力してください'
  puts '生徒の年齢を入力してください'

  # 登録した生徒の国語、数学、英語の点数を登録できるようにする
  puts "国語の得点は？"
  puts "数学の得点は？"
  puts "英語の得点は？"
end

def show_student_name
  # 登録された生徒の名前を番号を振って表示する
  puts '見たい生徒の番号を入力してください'

  # 選択された生徒の名前、年齢、国語、数学、英語の点数を表示できるようにする
  puts "名前:"
  puts "年齢:"
  puts "国語:"
  puts "数学:"
  puts "英語:"
end

students = []

while true
  puts '行いたい項目を選択してください'
  puts '[1]点数を登録する'
  puts '[2]点数を確認する'
  puts '[3]終了する'
  input = gets.to_i
  if input == 1
    registration_student
  elsif input == 2
    show_student_name
  elsif input == 3
    exit
  else
    puts '無効な値です'
  end
end
```
解答
```
def registration_student(students)
  # 生徒の名前と年齢を登録できるようにする
  student = {}
  puts '生徒名を入力してください'
  student[:name] = gets.chomp
  puts '生徒の年齢を入力してください'
  student[:age] = gets.chomp

  # 登録した生徒の国語、数学、英語の点数を登録できるようにする
  puts "国語の得点は？"
  student[:japanese] = gets.to_i
  puts "数学の得点は？"
  student[:math] = gets.to_i
  puts "英語の得点は？"
  student[:english] = gets.to_i
  students << student

end

def show_student_name(students)
  # 登録された生徒の名前を番号を振って表示する
  i = 0
  students.each do |student|
    puts "#{i}: #{student[:name]}"
    i += 1
  end
  puts '見たい生徒の番号を入力してください'
  num = gets.to_i

  student = students[num]
  # 選択された生徒の名前、年齢、国語、数学、英語の点数を表示できるようにする
  puts "名前: #{student[:name]}"
  puts "年齢: #{student[:age]}"
  puts "国語: #{student[:japanese]}"
  puts "数学: #{student[:math]}"
  puts "英語: #{student[:english]}"
end

students = []

while true
  puts '行いたい項目を選択してください'
  puts '[1]点数を登録する'
  puts '[2]点数を確認する'
  puts '[3]終了する'
  input = gets.to_i
  if input == 1
    registration_student(students)
  elsif input == 2
    show_student_name(students)
  elsif input == 3
    exit
  else
    puts '無効な値です'
  end
end
```
それぞれのパートに分けて解説

メソッド外の処理
```
students = []

while true
  puts '行いたい項目を選択してください'
  puts '[1]点数を登録する'
  puts '[2]点数を確認する'
  puts '[3]終了する'
  input = gets.to_i
  if input == 1
    registration_student(students)
  elsif input == 2
    show_student_name(students)
  elsif input == 3
    exit
  else
    puts '無効な値です'
  end
end
```
- students という配列を定義する
- メニューを表示し、入力待ちにさせる
- 入力した数値に対応するメソッドを実行する
- 以上のメニュー画面の表示を繰り返す

registration_student メソッド
```
def registration_student(students)
  # 生徒の名前と年齢を登録できるようにする
  student = {}
  puts '生徒名を入力してください'
  student[:name] = gets.chomp
  puts '生徒の年齢を入力してください'
  student[:age] = gets.chomp

  # 登録した生徒の国語、数学、英語の点数を登録できるようにする
  puts "国語の得点は？"
  student[:japanese] = gets.to_i
  puts "数学の得点は？"
  student[:math] = gets.to_i
  puts "英語の得点は？"
  student[:english] = gets.to_i
  students << student

end
```
- student というハッシュを定義する（3行目）
- 生徒名/生徒の年齢と国語/数学/英語の点数を入力させ、student というハッシュに代入する（5~15行目）
- students という配列に、先ほど定義した student というハッシュを追加する（16行目）

show_student_name メソッド
```
def show_student_name(students)
  # 登録された生徒の名前を番号を振って表示する
  i = 0
  students.each do |student|
    puts "#{i}: #{student[:name]}"
    i += 1
  end
  puts '見たい生徒の番号を入力してください'
  num = gets.to_i

  student = students[num]
  # 選択された生徒の名前、年齢、国語、数学、英語の点数を表示できるようにする
  puts "名前: #{student[:name]}"
  puts "年齢: #{student[:age]}"
  puts "国語: #{student[:japanese]}"
  puts "数学: #{student[:math]}"
  puts "英語: #{student[:english]}"
end
```
- 配列の情報のうち、登録されている生徒名を番号を添えて一覧表示する（3~7行目）
- 配列の要素であるハッシュの値を一覧表示させるために、要素番号を入力するように促し、 対応する要素を取得する（8~11行目）
- 指定された要素に含まれる各値を表示する （13~17行目）
# おわりに
問題数が進むに連れ、内容もボリューミーになってきたのでかなり時間がかかりました。。
一週間に渡るRubyの問題演習もこれで終了！
以降はpaiza等で技術研鑽を行いたいと思います!