フィヨルドブートキャンプに所属しているYukiです。 今回はフィヨルドブートキャンプで課された「ボウリングのスコアをターミナルに出力する」という課題を終了し、他の受講生の解答が見られるようになりましたので、その読解に取り組みます。 フィヨルドブートキャンプのメンターが作ったプログラムの読解です。
先頭部分
シバン
#!/usr/bin/env ruby
UNIXのスクリプトの #! から始まる1行目のこと。起動してスクリプトを読み込むインタプリタを指定する。
上記であれば「/usr/bin/envのrubyで起動してね」って内容。
イミュータブル化
# frozen_string_literal: true
要素の破壊的な変更を無効にする。 これによって不意に破壊的な変更を施してしまって不具合を起こしてしまうことを防ぐ。
本文
score = ARGV[0]
# ARGV[0]がコマンドラインオプションを表し、コマンドラインからオプションを配列で取得して代入。
scores = score.split(',')
# 取得した配列の値(6,3,9,0,0,3,8,2,7,3,X,9,1,8,0,X,6,4,5)をカンマ区切りでことなる配列にしている。
shots = scores.map { |s| s == 'X' ? 10 : s.to_i }
# scoreにmapメソッドを使って戻り値を配列として変数shotsに代入している。この際、値がXの場合は10に置換、そうでない場合は数値化している。
shots.each do |s|
frame << s
if frames.size < 10
if frame.size >= 2 || s == 10
# frameの値の数が2個以上もしくはブロック変数sが10のときに
frames << frame.dup
# frameの値(オブジェクト)を複製してframesに追加する
frame.clear
#frameの値を消去する
end
else # last frame
frames.last << s
end
end
# 変数shotsに代入されたスコアの値をごにょごにょして変数framesに代入する。スコア10個目(最終スコア)以前は変数frameに代入されたshotsの値を複製して代入しているが、最終スコアは直接shotsの値を代入している。10個目以前のスコアの場合は変数framesへの代入が終わると、変数frameの値は消去される。
スコアをフレームごとに配列に分けて変数framesに代入しています。このスコア計算においては1フレームの結果において計算が変動するのでフレームごとに分ける必要が生じます。その一方でストライクを出した場合はそこでそのフレームは終了する、という規則もあるのでそれに準じた形になるようにコードが書かれています。 でも、わざわざdupをつかったりする意図が読めないですが…。
point = 0
(0..9).each do |n|
#ボウリングのスコア回数分ループさせる。
frame, next_frame, after_next_frame = frames.slice(n, 3)
#多重代入を使って最大1回3ショットのスコアを変数に代入する。
next_frame ||= []
# 変数next_frameに値がなければ右辺を代入する
after_next_frame ||= []
left_shots = next_frame + after_next_frame
#変数left_shotsに配列のまま、2つの変数の値が代入される
if frame[0] == 10 # strike
point += frame.sum + left_shots.slice(0, 2).sum
# スコアを足し合わせて変数ポイントに代入。スコアの数値化のためにsumメソッドが使われている。
elsif frame.sum == 10 # spare
point += frame.sum + left_shots.fetch(0)
#fetchメソッドで指定番目の値を返すようにしている。
else
point += frame.sum
end
end
puts point
多重代入
複数の変数に対して一挙に値を代入できる。
a = 1, 2 #=> [1, 2] a, b = 1 a #=> 1 b #=> nil
今回はslice(pos, len)を使用して、1フレームごとに配列でスコアが代入されているframesから1フレームのスコアを抜き出している。
sliceの第一引数は抜き出す値の位置、第2引数はposからいくつの値を抜き出すか、となっている。