Project Euler Problem 19

Ivar Thorson bio photo By Ivar Thorson

Although using Conway’s Doomsday Algorithm is a fun party trick, I tend to hate calendar math. Worse, I hate Mondays too. So I guess this problem wasn’t written for me.

Still, how would we solve Project Euler Problem 19 in Clojure? Two obvious choices present themselves:

  1. Use a Java library that manipulates dates.
  2. Write a quick function by hand.

I chose option #2, although in retrospect #1 would have been a better idea.

(def days-per-mo      [31 28 31 30 31 30 31 31 30 31 30 31])
(def days-per-mo-leap [31 29 31 30 31 30 31 31 30 31 30 31])

(defn leap-year? [y]
  (or (zero? (mod y 400))
      (and (zero? (mod y 4))
           (not (zero? (mod y 100))))))

(defn accum [s]
  "Returns a sequence of the sums of elements up to each element in seq s. "
  (loop [e (first s)
         r (rest s)
         sums [0]]
    (if (nil? e)
      (rest sums)
      (recur (first r) (rest r) (conj sums (+ e (last sums)))))))

;; Any number with a mod 7 must be a monday, if we start on a monday.
(defn euler-19 []
  (count (filter #(= 0 (mod % 7))
                 (accum (concat [366] ;; First skip the year 1900
                                (mapcat #(if (leap-year? %)
                                           days-per-mo-leap
                                           days-per-mo)
                                        (range 1901 2001)))))))

(euler-19)

I am not actually certain that my hand-rolled solution is actually correct in the implementation (although it gave the correct answer), and prefer gnuvince’s solution posted on clojure-euler to my own.

Can anybody think of a better way to write accum? It looks like a useful function for other problems.