How do I check if a list is sorted in Racket?

advertisements

I am trying to define a function that takes a list as an argument and returns a boolean (#t or #f) indicating if the list is sorted in ascending order. The output of the function should be something like this:

(sorted? '(1 2 3 4 5))             ; => #t
(sorted? '(1 2 5 6 4))             ; => #f
(sorted? '("frank" "adam" "eve"))  ; => #f
(sorted? '("adam" "eve" "frank"))  ; => #t

Here's my attempt at a solution:

(define (sorted? lst)
  (cond
    [(< (length lst) 2]
    [(<= (car lst) (cadr lst)) (sorted? (cdr lst))]
    [(string? lst) (string<=? (car lst) (cadr lst)) (sorted? (cdr lst))]
    [else #f]))

I keep getting errors about the string part, and I can't seem to figure out what I'm doing wrong. Am I getting errors because I have two different types in the same definiton? I'm just assuming that since it's a conditional statement if the arguments in the list don't fit the condition, it should be ignored. I'm new at this and I need help. Please share your knowledge if you know what I'm doing wrong and what I need to do to fix this. Thanks.


Usually you don't want to mix numerical and lexical sort, but lets imagine for a second that you can compare any value:

(define (any<? a b)
  (cond ((and (number? a) (number? b)) (< a b))
        ((and (string? a) (string? b)) (string<? a b))
        ;; You can add types here. As a default we cast it
        ;; to string and compare it as string
        ;; NB! format is Racket specific
        (else (string<? (format "~A" a) (format "~A" b)))))

So lets sort something:

(define unsorted '(#\a  5 "hello" 9 (a c b) 10 (a b c) "50"))

;; NB: sort is racket specific, R6RS has list-sort
;; with the arguments in reverse order.
(define sorted (sort test any<?))
test2 ; ==> ((a b c) (a c b) 5 "50" 9 10 #\a "hello")

;; Using a named let with the current element
;; makes you only check null? once for each pair
(define (sorted? lst <)
  (or (null? lst)
      (let loop ((e (car lst)) (lst (cdr lst)))
        (or (null? lst)
            (and (not (< (car lst) e))
                 (loop (car lst) (cdr lst)))))))

(sorted? unsorted any<?) ; ==> #f
(sorted? sorted any<?)   ; ==> #t

You wouldn't use any<? but the specific comparison procedure for that data if you know the type of the elements in the list. E.g.

(sorted? '("a" "b" "cd") string<?) ; ==> #t
(sorted? '(4 7 3 5 9 3 4 6) <)     ; ==> #f