自転車とプログラミング

自転車メーカーに勤める会社員がプログラミングを学ぶ中で感じたことを書きます。最近サービス作りました。

人のプログラムを読解しよう ボウリングプログラム編01

フィヨルドブートキャンプに所属しているYukiです。 今回はフィヨルドブートキャンプで課された「ボウリングのスコアをターミナルに出力する」という課題を終了し、他の受講生の解答が見られるようになりましたので、その読解に取り組みます。 フィヨルドブートキャンプのメンターが作ったプログラムの読解です。

先頭部分

シバン

#!/usr/bin/env ruby

UNIXスクリプトの #! から始まる1行目のこと。起動してスクリプトを読み込むインタプリタを指定する。 上記であれば「/usr/bin/envrubyで起動してね」って内容。

イミュータブル化

# 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からいくつの値を抜き出すか、となっている。