いわゆる卍研究所。
In: Project Euler
13 1月 2010Project EulerをClojureでおさらいシリーズ。
Problem 19
日本語訳:
まず次の情報を与えておこう。(自分自身で確認してみるのもよいだろう)
- 1900年1月1日は月曜日
- 4月、6月、9月、11月は30日まで、
それ以外と2月を除く月は31日まである。
2月は28日までだが、閏年には29日までとなる。- 閏年は西暦の数字が4で割り切れる年におこる。
ただし400で割り切れず100で割り切れる場合は除く。20世紀(1901年1月1日〜2000年12月31日)には月の始めにくる日曜日がいくつあるか求めよ。
グレゴリオ暦を扱う問題。
以前Rubyで解いたもの:
#!/usr/bin/env ruby days_of_month = [[31,28,31,30,31,30,31,31,30,31,30,31], [31,29,31,30,31,30,31,31,30,31,30,31]] Monday = 1 Sunday = 0 def is_leap(year) year % 4 == 0 && year % 100 != 0 || year % 400 == 0 end count = 0 days_total = Monday + 365 # 1900 is not leap (1901..2000).each do |year| leap = is_leap(year) ? 1 : 0 (1..12).each do |month| count += 1 if days_total % 7 == 0 days_total += days_of_month[leap][month-1] end end p count
おもいっきり手続き型ですね。
今回のClojureでは:
(ns euler.p019) (def days-of-month [31 28 31 30 31 30 31 31 30 31 30 31]) (defn leap-year? "Return true if leap year." [year] (cond (zero? (rem year 400)) true (zero? (rem year 100)) false (zero? (rem year 4)) true :else false)) (defn leaps-from-origin "Return number of leap days form Jan-0 0000." [year month day] (+ (quot year 4) (- (quot year 100)) (quot year 400) (if (leap-year? year) (if (< month 3) -1 0) 0))) (defn days-total-from-origin "Return number of days from Jan-0 0000." [year month day] (+ (* 365 year) (apply + (take (dec month) days-of-month)) day (leaps-from-origin year month day))) (defn sunday? "Return true if date is sunday." [year month day] (let [sunday (rem (dec (days-total-from-origin 1900 1 1)) 7)] (= sunday (rem (days-total-from-origin year month day) 7)))) (println "Answer : " (count (filter #(apply sunday? %) (for [year (range 1901 2001) month (range 1 13)] [year month 1]))))
月初の日付を生成して、日曜日の判定を別関数にしました。
さてひととおり書いた後に 「Clojureにも日付を扱うライブラリくらいあるだろ」 と思い探してみましましたが見当たりませんでした。
こういうときはJavaを探すというお約束。→
ありました
グレゴリオ暦のほうじゃないと閏年の修正が異なるので注意。
これを使って書き直すと:
(ns euler.p019 (:import (java.util GregorianCalendar))) (defn sunday? "Return true if date is sunday." [year month day] (let [date (doto (GregorianCalendar.) (.set year (dec month) day))] (= (. date get GregorianCalendar/DAY_OF_WEEK) GregorianCalendar/SUNDAY))) (println "Answer : " (count (filter #(apply sunday? %) (for [year (range 1901 2001) month (range 1 13)] [year month 1]))))
かなりスッキリしました。
私 manjilab のポータル的サイトになっております。日々気付いたこと、考えたこと、発表したいものを載せていきます。