Buggy loops can also be avoided by reducing the problem. These leap-year cases
are less troubling if the function that really matters is used: the days in a
given year.
(define (days-in-year year) (if (= 0 (remainder year 4)) 366 365))
(define (yr-da yr da)
(cond ((<= da (days-in-year yr)) (values yr da)) ; Done: da is
within the year, yr.
(else (yr-da (+ 1 yr) (- da (days-in-year yr)))))) ; reduce da by days
in the year, yr.
A little rearranging can remove the inefficiency of computing days-in-a-year
twice.
But how would I arrive at that solution by using the blue book?
rac
On May 30, 2011, at 8:31 PM, Matthias Felleisen wrote:
>
> On May 30, 2011, at 9:11 PM, Richard Cleis wrote:
>
>> An HtDP solution would more likely describe the data first (days are less
>> than 366, equal to 366, or greater than 366). One cond would handle all
>> three, two would terminate, and the the last would continue the
>> accumulation.
>
> I had actually written the program in HtDP style, and yes, the accumulator
> made it clear that the if was wrong:
>
> ;; N -> [List N N N]
> (define (my-date days)
> ;; N N -> N
> ;; accu: y is the number of complete years between days and d, plus 1980
> ;; gen. recursion (subtract number of days until the number is w/i a year)
> (define (year d y)
> (cond
> [(<= d 365) (values y d)]
> [else (if (leap-year? y)
> (cond
> [(> d 366) (year (- d 366) (+ y 1))]
> [(= d 366) (values y d)])
> (year (- d 365) (+ y 1)))]))
> (define-values (the-year remaining-days-in-year) (year days 1980))
> ;; N N -> N
> ;; accu: m is the months between remaining-days-in-year and d, starting in
> Jan
> ;; gen. recursion (subtract number of days until w/i a month)
> (define (month d m)
> (cond
> [(<= d (month->days m the-year)) (values m d)]
> [else (month (- d (month->days m the-year)) (+ m 1))]))
> (define-values (the-month remaining-days-in-month) (month
> remaining-days-in-year 1))
> ;; -- IN --
> (list the-year the-month remaining-days-in-month))
>
> ;; N -> Boolean
> ;; simplistic definition
> (define (leap-year? y)
> (= (remainder y 4) 0))
>
> ;; N N -> N
> ;; the number of days in month m for year y
> (define (month->days m y)
> (case m
> [(1 3 5 7 8 10 12) 31]
> [( 4 6 9 11) 30]
> [else (if (leap-year? y) 29 28)]))
>
> ;; -- testing ---
>
> (check-equal? (my-date 364) '(1980 12 29))
> (check-equal? (my-date 366) '(1980 12 31))
> (check-equal? (my-date (+ 365 366)) '(1981 12 31))
> (check-equal? (my-date (+ 365 365 365 366)) '(1983 12 31))
>
> (define long
> (let ([years# (- 2009 1980)])
> (apply + (build-list years# (lambda (i) (define y (+ i 1980)) (if
> (leap-year? y) `366 365))))))
>
> (check-equal? (my-date long) '(2008 12 31))
>
>
>> In other words, the possibility of that hard-to-read bug doesn't exist.
>
> There are bugs in FP programs for sure, but the [while-]loop exit bugs
> disappear.
>
>
_________________________________________________
For list-related administrative tasks:
http://lists.racket-lang.org/listinfo/users