自転車とプログラミング

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

人のプログラムを読解しよう カレンダープログラム編01−03

前回からの続きです

初回はこちら

前回はこちら

読解

コマンドラインオプションの取得

options = ARGV.getopts("m:", "y:")
#optparseライブラリからgetoptsメソッドを使用してコマンドラインオプション(引数)を変数optionsに代入する

getoptsメソッドはoptparseライブラリのメソッドなので、事前にrequire 'optparse'と記述する必要があります。 optparseはOptionParserの略でOptionはコマンドラインオプションのこと。Parserはパース(データをプログラムで使えるように変換すること)を指す。なのでOptionParserでコマンドラインオプションをプログラムで使えるようにするよ〜、というものだとわかる。

また、ARGVはアーギュメントヴェクター(argument vector)の略で配列オブジェクトのこと。だけどgetoptsメソッドはハッシュを返す。謎。

getoptsメソッドはショートオプション、ロングオプションを取ることができる。どちらも引数を取る場合は"m:"のように:をつける。:をつけない場合は引数をとらず、オプションとして入力されたかどうかで評価(true/false)される。

コマンドラインオプションから日付を取得 or デフォルト日付の設定

month = options["m"] || today.month
year = options["y"] || today.year
# 前段で取得した変数options内ハッシュのmとyを、年月に分けて変数month/yearに代入する。その際に||を使ってキーm/yの値がない場合は、今日の日付の年月を代入する。

コマンドラインオプションから自由な年月設定を受け付けるか、今日の年月を自動入力するか、という部分。演算子||を使うことで非常にシンプルに纏められていて感激。 ||は左辺を評価して真であれば左辺を、偽(false)であれば右辺を返す演算子式。 左辺を前段のコマンドラインオプション、右辺を先行して取得したDate.todayオブジェクトにmonth/yearメソッドを使うことでそれぞれ年月を代入している。

月の日にちを取得

first_day = Date.new(year, month, 1)
#月の初日を取得
last_day = Date.new(year, month, -1)
#月の最終日を取得
days = ([" "] * first_day.wday) + (1..last_day.day).to_a
#変数daysに月初日までの曜日数分の空白とその月の日にちを配列で代入する

変数first_day/last_dayは、Dateオブジェクトを作って前段の変数year/monthと初日を表す1と最終日を表す-1を取得したものを代入している。 -1に関しては、newメソッドを使った際に負の値を指定するとその月の最終日から逆算した日数を返す。4月であれば−1で4/30、-2で4/29といった感じ。

days = ...はカレンダー用に日付を取得している。このプログラムではカレンダーを行ごとに分けて出力する発想で組まれており、その月の日付と月初の日付が始まるまでの曜日分(月曜スタートなら日曜分の1個)の空白を取得している。 1..last_day.dayは先程取得した月の最終日が代入された変数last_dayをつかって範囲式を組み立てることで1〜最終日までの配列を取得している。 .to_aメソッドは値を配列にするメソッド。

カレンダー形式で出力

puts "#{month}月 #{year}年".center(20)
#変数month月 変数year年を中央表示で出力(カレンダーの見出し)
puts "日 月 火 水 木 金 土"
days.each_slice(7) do |week|
#変数days(月の日付+月初日までの空白)の値を7つごとに区分して以下のブロック変数weekに代入して以下の処理をする
  week.each do |day|
  #ブロック変数weekの値をブロック変数dayに代入してそれぞれ以下の処理をする
    if today?(today, year, month, day)
     #メソッドtoday?に一日ごとの年月日が代入される、今日であればtrue、それ以外はfalseを返す
      print convert_reverse_color(day)
      #今日の日付を文字色背景色を反転させる
    else
      print arrange_to_three_chars(day)
      #日にちの出力。arrange_to_three_charsメソッドによってひとつの日付ごとに3文字になるように調整される
    end
  end
  puts "\n"
  #値7個の終わりに改行
end

最後にカレンダー形式で出力する部分の読解。 .centerメソッドは引数分の文字数でレシーバを中央寄せで表示する(引数-レシーバの文字数分の空白を生成する)。これで見出しを中央表示にしている。 .each_slice()は配列をそれぞれ処理するeachメソッドに、値をまとめるsliceをつけたもの。引数個分に配列の値をまとめてブロック内の処理を実行する。この場合は値を7個(一週間)にまとめている。 そこからさらにeachメソッドを使うことで行ごとに処理を与えている。

ネストしたeachメソッド内のifは予め記述したtoday?メソッドを使用して真偽値を取得、評価している。trueの場合はtoday?メソッドでは「今日」に当たるので文字色と背景色を反転する処理をする。 今日でない場合は文字数調整をするメソッドに飛ばしている。 最後に改行を表す\nを記述することで日にちを7日ずつ出力してカレンダーとして出力している。

まとめ

以上、読解でした。 コードがシンプルなので性格な意味を理解するにも時間がかかりました。 また、コードを簡素化させるのはテクニックによる部分が大きいとも思いました。 知ってれば簡素にできるけど、知らなければひたすら長大なコードを書かざるを得ないという…。 プログラミングって小手先も大事だなーと改めて思った次第です。