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

Reply via email to