🐡

Ruby初学者が解く「カレンダー作成問題」

2022/10/25に公開

はじめに

Ruby初学者のPHPerです。
プロを目指す人のためのRuby入門[改訂2版]を一周した後に何か良い勉強方法がないか調べていたところ、下記の記事を見つけたのでやってみました。
参考:アウトプットのネタに困ったらこれ!?Ruby初心者向けのプログラミング問題を集めてみた(全10問)

既に多くの方がネット上に解答を載せてくれていますが、個人的な備忘録も兼ねて残しておきたいと思います。
今回は「カレンダー作成問題」です。

カレンダー作成問題とは

元ネタは「たのしいRuby」に載っています。
https://www.amazon.co.jp/exec/obidos/ASIN/4797399848/junic05-22/
問題の内容を下記です。

Date クラスを使って、今月の1日と月末の日付と曜日を求め、次のような形式でカレンダーを表示させてください

      April 2013
 Su Mo Tu We Th Fr Sa
     1  2  3  4  5  6
  7  8  9 10 11 12 13
 14 15 16 17 18 19 20
 21 22 23 24 25 26 27
 28 29 30

バージョンなど

  • Ruby v3.1.2

ソース

calendar.rb
require 'date'

# class Calendar
class Calendar
  DEFAULT_DATE = Date.today

  attr_reader :target_date

  def initialize(target_date = DEFAULT_DATE)
    @target_date = target_date
  end

  def print_calendar
    # 中央揃えした年月を出力
    puts @target_date.strftime('%B %Y').center(20)

    # 曜日一覧を出力
    puts 'Su Mo Tu We Th Fr Sa'

    # 月末と月初の日付を取得
    this_year = @target_date.year
    this_month = @target_date.month
    first_day = Date.new(this_year, this_month, 1)
    last_day = Date.new(this_month, this_month, -1)

    # 冒頭の空白部分を出力
    first_day_wday = first_day.wday
    blank = '   ' * first_day_wday
    print blank

    # カレンダーの日付部分を出力
    print_date(last_day, first_day_wday)
  end

  private

  def print_date(last_day, first_day_wday)
    (1..last_day.day).each do |day|
      printf('%2d ', day)
      puts "\n" if ((first_day_wday + day) % 7).zero?
    end
    puts "\n"
  end
end

# 今月のカレンダー
this_month_calendar = Calendar.new
this_month_calendar.print_calendar

上記のコードの出力は下記です。

    October 2022    
Su Mo Tu We Th Fr Sa
                   1 
 2  3  4  5  6  7  8 
 9 10 11 12 13 14 15 
16 17 18 19 20 21 22 
23 24 25 26 27 28 29 
30 31 

説明

  1. クラス化
    クラス化したことで指定月のカレンダーを簡単に出力できます。
    2022年11月のカレンダーを表示させたい場合、下記です。
next_month_calendar = Calendar.new(Date.new(2022, 11, 1))
next_month_calendar.print_calendar
  1. private メソッド化
    print_calendarメソッドが冗長していたので簡潔に書くために、print_dateメソッドを作成しました。
    eachの中は短いものの、複数行あるためdo ... endで書いてます。
    参考:プロを目指す人のためのRuby入門[改訂2版]
    また、if文の中の処理は一行のみであり、内容もシンプルなため、後置ifにしました。
  def print_date(last_day, first_day_wday)
    (1..last_day.day).each do |day|
      printf('%2d ', day)
      puts "\n" if ((first_day_wday + day) % 7).zero? # 後置if
    end
    puts "\n"
  end

残課題

  • テストの作成(後日追記予定)

参考文献

GitHubで編集を提案

Discussion