2019-05-27
Ruby手習い(Dateクラス)
アウトプットのネタに困ったらこれ!?Ruby 初心者向けのプログラミング問題を集めてみた(全 10 問) - give IT a try
上記記事のカレンダー作成問題。
出力結果
p-064% ruby calendar.rb
May 2019
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
自分で書いたコード
require 'date'
def main
puts calendar
end
def calendar
today = Date.today
title = today.strftime("%B %Y").center(20)
head = "Su Mo Tu We Th Fr Sa"
last_day = Date.new(today.year, today.month + 1, -1).day
wday = Date.new(today.year, today.month, 1).wday
body = " " * wday
d = 1
(1..last_day).each do
body << d.to_s.rjust(2)
d += 1
wday += 1
body << (wday % 7 == 0 ? "\n" : " ")
end
[title, head, body].join("\n")
end
if __FILE__ == $0
main
end
- 文字を表示するだけなので 1 つ 1 つの Date インスタンスつくらなくていいやと考えた。
- あと細かくメソッドに分割するのは YAGNI でやらなくていいやと考えた。
出題者による回答例
require 'date'
class CalendarRenderer
DAY_LENGTH = 3
WEEK_LENGTH = DAY_LENGTH * 7
def initialize(year, month)
@first_date = Date.new(year, month, 1)
end
def to_s
(header_rows + body_rows).join("\n")
end
private
def body_rows
split_pattern = /.{#{DAY_LENGTH},#{WEEK_LENGTH}}/
body_text.scan(split_pattern)
end
def body_text
first_week_offset + calendar_text
end
def first_week_offset
' ' * @first_date.wday * DAY_LENGTH
end
def calendar_text
last_day = @first_date.next_month.prev_day.day
rjust_all 1..last_day
end
def header_rows
[month_year, sun_to_sat]
end
def month_year
indent_length = 1
@first_date.strftime("%B %Y").center(WEEK_LENGTH + indent_length).rstrip
end
def sun_to_sat
rjust_all %w(Su Mo Tu We Th Fr Sa)
end
def rjust_all(enum)
enum.to_a.map{|e| e.to_s.rjust(DAY_LENGTH) }.join
end
end
- (ちょっと仕様=インターフェースがちがう)
- 1 日当たりの幅を定数で指定できるようにしてる。
- 1 日から最終日までの文字列をつくってから、カレンダーの幅で split → \n で join なるほど、これが最大の違い。まず材料をつくって、それを整形するというこっちの処理の方が拡張性高そう。発想できたかったな。
学んだこと
- ふだん Ruby の組み込みじゃないんだな。
String#center
をはじめて知った。