Tuesday, 27 March 2012



Exercise 160: Design a program that sorts lists of mail messages by date:
(define-struct mail (from date message))
; Mail Message is a structure:
;  (make-mail String Number String)
; interp. (make-mail f d m) represents text m sent by
; fd seconds after the beginning of time
Also develop a program that sorts lists of mail messages by name. To compare two strings alphabetically, use the string<? primitive.
***
This exercise was pretty easy - the answer to most of this was given more or less directly in the previous example of sorting a list of numbers. To make this more challenging I tried to do this without referring back to the previous example.




First I defined a series of structs that contain some predefined emails. This is just to make to check-expects easier to code and to read.




; Number List-of-numbers -> List-of-numbers

(define-struct mail (from date message))
(define FIRST (make-mail "dave"  0 "hello"))
(define SECOND (make-mail "john" 10 "hello"))
(define THIRD (make-mail "andrew" 20 "hello"))
(define FOURTH (make-mail "andrew" 40 "hello"))


These check-expects are pretty similar to the previous example - I've just changed them to look at mails instead.


; List-of-mails -> List-of-mails
; produces a version of alom, sorted by date in descending order
(check-expect (sort-date-> empty) empty)
(check-expect (sort-date-> (list THIRD FOURTH FIRST)) 
              (list FOURTH THIRD FIRST))
(check-expect (sort-date-> (list THIRD SECOND FIRST)) 
              (list THIRD SECOND FIRST))
(check-expect (sort-date-> (list FIRST SECOND THIRD)) 
              (list THIRD SECOND FIRST))

Sort-date is again almost identical to the standard previous example. The only difference is that we are sorting by date and inserting by date.

(define (sort-date-> alom)
  (cond
    [(empty? alom) empty]
    [else (insert-date (first alom) (sort-date-> (rest alom)))]))



Here we insert our date in to the already sorted list. To simplify this I have made a helper function more-recent? which will compare the dates on two emails and let us know which is more recent.


; insert n into the sorted list of date alon
(define (insert-date n alon)
  (cond [(empty? alon) (cons n empty)]
        [else (cond [(more-recent? n (first alon)) (cons n alon)]
                    [else (cons (first alon) 
                                (insert-date n (rest alon)))])]))


This is the helper function to determine which of two emails is more recent. We could do this inline in the insert-date method, but this lends itself to more clarity. 

(define (more-recent? email-1 email-2)
  (>= (mail-date email-1) (mail-date email-2)))



The sort order is not specified, so I will assume we are sorting from most recent -> least recent                                

(check-expect (insert-date FIRST empty) (list FIRST))
(check-expect (insert-date FIRST (list SECOND )) 
              (list SECOND FIRST))
(check-expect (insert-date SECOND (list FIRST)) 
              (list SECOND FIRST))
(check-expect (insert-date SECOND (list THIRD FIRST)) 
              (list THIRD SECOND FIRST));;;




; For the second part of the question we need to sort by name. The easiest way to do this would be to pass the insert and comparison methods into our sort function so that the same function could sort both names and dates. However we haven't been told how to do this in the text yet, so I assume they don't want us to do this.


As this is so similar to sorting by date I won't document what I am doing in as much detail.


I have created some more structs for this - I could have rigged the original ones so that they were both alphabetically and date sorted, but creating new ones seems to be more honest.

(define NFIRST (make-mail "andrew"  20 "hello"))
(define NSECOND (make-mail "bob" 410 "hello"))
(define NTHIRD (make-mail "charlie" 20 "hello"))
(define NFOURTH (make-mail "dave" 4 "hello"))

; List-of-mails -> List-of-mails
; produces a version of alom, sorted by name in descending order
(check-expect (sort-name-> empty) empty)
(check-expect (sort-name-> (list NTHIRD NFOURTH NFIRST)) 
              (list NFOURTH NTHIRD NFIRST))
(check-expect (sort-name-> (list NTHIRD NSECOND NFIRST)) 
              (list NTHIRD NSECOND NFIRST))
(check-expect (sort-name-> (list NFIRST NSECOND NTHIRD)) 
              (list NTHIRD NSECOND NFIRST))

(define (sort-name-> alom)
  (cond
    [(empty? alom) empty]
    [else (insert-by-name (first alom) (sort-name-> (rest alom)) )]))

; insert n into the sorted list of mails alom
(define (insert-by-name n alom)
  (cond [(empty? alom) (cons n empty)]
        [else (cond [(alphabetically-earlier? n (first alom)) 
                     (cons n alom)]
                    [else (cons (first alom) 
                                (insert-by-name n (rest alom)))])]))

; sort order not specified, so will assume sort from most recent -> least recent                                
(check-expect (insert-by-name NFIRST empty) (list NFIRST))
(check-expect (insert-by-name NFIRST (list NSECOND ))
              (list NSECOaND NFIRST))
(check-expect (insert-by-name NSECOND (list NFIRST)) 
              (list NSECOND NFIRST))
(check-expect (insert-by-name NSECOND (list NTHIRD NFIRST))
              (list NTHIRD NSECOND NFIRST))

; utility function to determine which of two emails is more recent
(define (alphabetically-earlier? email-1 email-2)
  (string>? (mail-from email-1) (mail-from email-2)))


No comments:

Post a Comment