123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- ;;; Counting Sundays
- ;;; Problem 19
- ;;; You are given the following information, but you may prefer to do
- ;;; some research for yourself.
- ;;; - 1 Jan 1900 was a Monday.
- ;;; - 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.
- ;;; How many Sundays fell on the first of the month during the
- ;;; twentieth century (1 Jan 1901 to 31 Dec 2000)?
- ;; IDEA: Using the given information, one could calculate ahead until
- ;; the end date in steps of 7 days, each time checking, whether it is
- ;; the first day of a month.
- ;; IDEA: Cheating: Using Guile's date functions, one could probably
- ;; query for each first of a month, whether or not it was a Sunday.
- ;; IDEA: Instead of going in days steps, one could go in variable
- ;; sized steps from first of a month to the first of the next
- ;; month. If the difference in days is divisable by 7, then it is the
- ;; same day as the previous first of a month.
- ;; IDEA: Calculate each first of the month and store it as a
- ;; days-from-start-date number, check how many of those modulo 7 are
- ;; congruent to 0.
- (import
- (except (rnrs base) let-values map)
- (only (guile)
- lambda* λ
- ;; printing
- display
- simple-format
- ;; command line arguments
- command-line
- current-input-port
- current-output-port)
- ;; hash tables
- (srfi srfi-69)
- ;; vectors
- (srfi srfi-43)
- (lib debug-utils))
- (define DAYS-IN-MONTH
- (list->vector
- '(31 ; January
- 28 ; February
- 31 ; March
- 30 ; April
- 31 ; May
- 30 ; June
- 31 ; July
- 31 ; August
- 30 ; September
- 31 ; October
- 30 ; November
- 31 ; December
- )))
- (define MONTH-NAMES
- (alist->hash-table
- '((0 . january)
- (1 . february)
- (2 . march)
- (3 . april)
- (4 . may)
- (5 . june)
- (6 . july)
- (7 . august)
- (8 . september)
- (9 . october)
- (10 . november)
- (11 . december))))
- (define WEEKDAY-NAMES
- (alist->hash-table
- '((0 . monday)
- (1 . tuesday)
- (2 . wednesday)
- (3 . thursday)
- (4 . friday)
- (5 . saturday)
- (6 . sunday))))
- (define start-date-day 0)
- (define leap-year?
- (λ (year)
- (cond
- ;; "A leap year occurs on any year evenly divisible by 4, but not
- ;; on a century unless it is divisible by 400."
- [(= (remainder year 4) 0)
- (or (= (remainder year 400) 0)
- (not (= (remainder year 100) 0)))]
- [else #f])))
- (define january?
- (λ (month)
- (= month 0)))
- (define february?
- (λ (month)
- (= month 1)))
- (define december?
- (λ (month)
- (= month 11)))
- (define sunday?
- (λ (days-count)
- ;; Weeks do not change at all and always have 7 days. -> When
- ;; day-count modulo 7 is 6, then the day is a Sunday.
- (= (remainder days-count 7) 6)))
- (define number-of-days-in-month
- (λ (year month)
- (cond
- [(and (february? month)
- (leap-year? year))
- 29]
- [else
- (vector-ref DAYS-IN-MONTH month)])))
- (define next-month
- (λ (month)
- (remainder (+ month 1)
- (vector-length DAYS-IN-MONTH))))
- (define end-date?
- (λ (year month)
- (and (= year 2001)
- (january? month))))
- (define count-first-of-month-sundays
- (λ ()
- (let iter ([year 1901]
- [month 0]
- ;; Start with a Tuesday.
- [days-count 1]
- [fom-sundays-count 0])
- (debug "days count:" days-count)
- ;; Move towards 31st of December 2000, by adding the number of
- ;; days in each month of a year, so that we always move from
- ;; first of one month to first of the next month. We do not need
- ;; to look at any other days. Each iteration we need to check,
- ;; if we are at a Sunday.
- (cond
- [(end-date? year month) fom-sundays-count]
- [else
- (cond
- [(sunday? days-count)
- (displayln year month "is a" (hash-table-ref WEEKDAY-NAMES (remainder days-count 7)))
- (iter (if (december? month) (+ year 1) year)
- (next-month month)
- ;; Add number of days in this month to get to the next
- ;; first of the month.
- (+ days-count (number-of-days-in-month year month))
- (+ fom-sundays-count 1))]
- [else
- (displayln year month "is a" (hash-table-ref WEEKDAY-NAMES (remainder days-count 7)))
- (iter (if (december? month) (+ year 1) year)
- (next-month month)
- ;; Add number of days in this month to get to the next
- ;; first of the month.
- (+ days-count (number-of-days-in-month year month))
- fom-sundays-count)])]))))
- (displayln "number of first of the month sundays:" (count-first-of-month-sundays))
|