Reputation: 116
As the title suggest i am trying to write a scheme function that checks if all elements of a list are unique. I have written some code that i think should work:
(define are-all-unique?
(lambda (v)
(if (member (car v) (cdr v))
#f
(if (pair? v)
(are-all-unique? (cdr v))
#t))))
and it works fine in the case where it is false, but if i write:
(are-all-unique? '(1 2 3))
it returns:
Exception in car: () is not a pair
Does anyone have a clue on how to fix this, and what i am doing wrong? :)
Upvotes: 2
Views: 1759
Reputation: 362
Well, as your error message states, you're attempting to pass the empty list to car
. As for why that's happening, let's take a look at your code.
As we know, we need to pass a cons
cell to car
and cdr
for them to work. We check for this property using pair?
, which you are doing. The problem is when you're doing it.
The best way to debug a function like this is trace through what happens when you pass the bad input (in this case '()
) to the function. The first thing it will do is run the test in your if
statement: (member (car v) (cdr v))
. Now, since the input we're tracing is the empty list, this will fail every time. What you want is something more like this:
;; are-all-equal? is a poor choice of function name, by the way, since that's
;; not really what this function checks
(define are-all-unique?
(lambda (v)
(if (pair? v)
; The only way that this branch will ever execute is
; if (pair? v) is true, so we know that (car v) and (cdr v)
; will never raise any exceptions
(and (not (member (car v) (cdr v)))
(are-all-unique? (cdr v)))
#t)))
The thing to note here is the control flow structure of this version of the function: a proper list is either a cons
cell or the empty list, so we have two cases in our function (one for each possibility). When writing functions which deal with lists, one should (for the most part) decide what you want to do in the nonempty and empty cases, and make sending the list argument to the appropriate case the first thing you do inside of the function. Such an approach is appropriate when doing structural recursion (i.e., in the words of one of my freshman professors, 'the structure of your data informs the structure of your code').
Upvotes: 4