Clojure Euler: Problem 019

How many Sundays fell on the first of the month during the twentieth century (1 Jan 1901 to 31 Dec 2000)?

Permalink: http://projecteuler.net/problem=19

Project Euler also gives following information:

Thirty days has September,
April, June and November.
All the rest have thirty-one,
Saving February alone,
Which has twenty-eight, rain or shine.
And on leap years, twenty-nine.
* A leap year occurs on any year evenly divisible by 4, but not on a century unless it is divisible by 400.

Implementin calendars logic is great, but java interoperability is much better!

First of all, take standard java GregorianCalendar class. We can import it almost the same way as we use require

(import [java.util GregorianCalendar])

To create an object use function new

(new GregorianCalendar)

But it is more idiomatic way to create java objects with special syntax: period after class name

(GregorianCalendar.)

To configure calendar object to specific date we need to set some its properties. Setters syntax is following (assuming calendar is an object of calendar):

Java

calendar.set(GregorianCalendar.YEAR, 2013);

Clojure

(.set calendar GregorianCalendar/YEAR 2013)

Executing lot of setters is the same boilerplate as in java:

(do
  (.set calendar GregorianCalendar/YEAR 2013)
  (.set calendar GregorianCalendar/MONTH 11)
  (.set calendar GregorianCalendar/DAY_OF_MONTH 1))

To avoid repetition of object name we can use doto macro:

(doto (GregorianCalendar.)
  (.set GregorianCalendar/YEAR 2013)
  (.set GregorianCalendar/MONTH 11)
  (.set GregorianCalendar/DAY_OF_MONTH 1))

This macro magically performs the same set of operations as above, but in more readable way.

Now we can create calendar objects for the first of the specific month of the specific year

(defn calendar-for [year month]
  (doto (GregorianCalendar.)
    (.set GregorianCalendar/YEAR year)
    (.set GregorianCalendar/MONTH month)
    (.set GregorianCalendar/DAY_OF_MONTH 1)))

Just iterate through all such objects in date range given in problem definition and count Sundays.

(reduce +
  (for [year (range 1901 (inc 2000)) month (range 1 (inc 12))]
    (let [c (calendar-for year month)]
          (if (= GregorianCalendar/SUNDAY 
                 (.get c GregorianCalendar/DAY_OF_WEEK)) 1 0))))

Problem solved!

P.S. Java interoperability is ugly but still useful.

mishadoff 10 July 2013
blog comments powered by Disqus