Testing Set Operations

When Order Doesn't Matter...

As some of you have noticed, testing the functions you write for Problem 3 and Problem 4 on Homework 7 creates a new problem for us: The functions set-add and set-union returns sets, which are lists. But the items in a set can be in any order, so check-equal? is too restrictive.

(check-equal? (set-union '(a b) '(x y z))
              '(a b x y z))

The two lists do not have to be equal; they only need to contain the same items as each other.

Rackunit provides a higher-order check operator that allows us to pass our own test predicate:

(check set-equals? (set-union '(a b) '(x y z))
                    '(a b x y z))

Now all we need is a function set-equals? that takes two sets as arguments and returns true if they contain the same items as each other, and false otherwise:

> (set-equals? '(a b x y z) '(z y x a b))
#t
> (set-equals? '(a b x y z) '(a b w y x))
#f

How would you write a recursive (set-equal? S1 S2) function?

We might try to use the same structurally recursive approach that we use for (set-union S1 S2) and (set-subset? S1 S2) on the homework. The challenge is to find a way to process on of the sets item by item. (If you are up for the challenge, give it a try.)

However, once you've written set-subset?, we have a shortcut. You may remember this definition from another course: two sets S1 and S2 are equal to one another if and only if S1 S2 and S2 S1. So:

(define set-equals?
  (lambda (S1 S2)
    (and (set-subset? S1 S2)
         (set-subset? S2 S1))))

This enables us to write tests using Rackunit's check operator as above. And that's what I'll do when I evaluate your Homework 7 solutions.

My set-equals? is not quite that simple, though. I am testing student code, which creates several wrinkles for me. One is this: I can't use the set-subset? function in your submission to implement set-equals? until I know that set-subset? is correct! This wrinkle can be ironed out with a local function...