Sunday, 26 April 2009
The Mod Operator and Zeller's Congruence
Java's built in mod operator (
Java:
Clojure:
Java:
Clojure:
Clojure:
The Java behaviour is explained more clearly by the JLS, here.
Why would the behaviour of negative numbers and the mod operator ever be useful? One algorithm that uses this is Zeller's Congruence which allows you to determine the day of the week if you know the date.
Defining the algorithm in Clojure is simple:
The algorithm is slightly strange in that January and February need to be treated as the 13th and 14th of the previous month. We can encapsulate this ugliness behind a nicer interface.
Now we can try this out:
Of course, any sane person would actually use a built in library function to do this! My rambling point is just that, Java
%
) behaves differently than the Clojure mod
function with negative operatorsJava:
System.out.println("5 % 2 = " + (5 % 2)); // 1
Clojure:
(mod -7 2) ; 1
Java:
System.out.println("-2 % 7" + (-2 % 7)); // -2
Clojure:
(mod -2 7) ; 5
Clojure:
(rem -2 7) ; -2
The Java behaviour is explained more clearly by the JLS, here.
rem
should be the Clojure equivalent if you want to support the same behaviour as Java.Why would the behaviour of negative numbers and the mod operator ever be useful? One algorithm that uses this is Zeller's Congruence which allows you to determine the day of the week if you know the date.
Defining the algorithm in Clojure is simple:
(def days
{0 'Saturday
1 'Sunday
2 'Monday
3 'Tuesday
4 'Wednesday
5 'Thursday
6 'Friday})
(defn zeller
"Zeller's congruence"
[day-of-month month year century]
(get days
(mod (+ day-of-month
(int (/ (* 26 (inc month)) 10))
year
(int (/ year 4))
(int (/ century 4))
(- (* 2 century))) 7)))
The algorithm is slightly strange in that January and February need to be treated as the 13th and 14th of the previous month. We can encapsulate this ugliness behind a nicer interface.
(def months
['January
'February
'March
'April
'May
'June
'July
'August
'September
'October
'November
'December])
(def month-to-number
(zipmap months [13 14 3 4 5 6 7 8 9 10 11 12]))
(defn day-of-week
[day month year]
(let [month-num (get month-to-number month)
year-int (if (or (= month-num 13) (= month-num 14)) (dec year) year)]
Now we can try this out:
user> (day-of-week 22 'November 1963) ; JFK assassination
Friday
user> (day-of-week 20 'July 1969) ; Apollo Moon landing
Sunday
user> (day-of-week 27 'April 2009)
Monday
Of course, any sane person would actually use a built in library function to do this! My rambling point is just that, Java
%
is not the same as Clojure mod
!Labels: clojure
Subscribe to Posts [Atom]