An alternative to less would have been less_or_equal, in which case two objects a and b would be equal if and only if both a is less than or equal to b and b is less than or equal to a. The choice is a matter of taste.
Another difference is that, while insert in the unsorted case did not care exactly where the object was inserted, in the sorted case, it must be inserted in exactly one place. We must make sure that the sorted version of find stops at this exact position in the case where the element is not on the list.
#ifndef SLSET_H #define SLSET_H /* We use unsorted linked lists to implement sets of elements, that can handle only equality */ struct slset; typedef struct slset *slset; /* create an empty slset */ extern slset slset_create(int (*less)(void *, void *)); /* return a true value iff the element is a member of the slset */ extern int slset_member(slset l, void *element); /* insert an element into an slset. The element must not already be a member of the slset. */ extern void slset_insert(slset l, void *element); /* delete an element from an slset. The element must be a member of the slset. */ extern void slset_delete(slset l, void *element); #endifNotice that we have replaced equal by less. Otherwise, this header file is identical to that of ulset.
struct slset { int (*less)(void *, void *); list elements; };If we are careful, we should have to make only minor modifications to the interface functions compared to the unsorted case. The only function needing major revision would be the find function. Recall that in the unsorted case, the find function returns pointer to a list (not a pointer to a cell). Dereferencing that pointer gives the information about the presence of the element we were looking for. If dereferencing the pointer gives NULL, then the element is not in the list. If not, then the element is present in the cell obtained by dereferencing the pointer. Here is the find function for the unsorted case:
list * find(list *lp, void *e, int (*equal)(void *, void *)) { for(; *lp && !equal((*lp) -> element, e); lp = &((*lp) -> next)) ; return lp; }For the sorted case, we simply replace that with:
list * find(list *lp, void *e, int (*less)(void *, void *)) { for(; *lp && less((*lp) -> element, e); lp = &((*lp) -> next)) ; return lp; }We loop until either the list is exhausted, meaning that the element we were looking for was greater than or equal to all the elements of the list, or until we found a position such that the element of that position is either greater than or equal to the element we are looking for. This is exactly what we want. If this version of find is called with an element already on the list, then dereferencing the pointer gives a non-NULL value equal to the one we are looking for. If called with an element that is not on the list, find will return a pointer to a place where that element should be inserted.
Now, all we have to do is update the interface functions to take advantage of this new version of find. Here is the entire implementation file:
#include "slset.h" #include "list.h" #include < assert.h> #include < stdlib.h> struct slset { int (*less)(void *, void *); list elements; }; slset slset_create(int (*less(void *, void *))) { slset l = malloc(sizeof(struct slset)); l -> elements = NULL; l -> less = less; return l; } static list * find(list *lp, void *e, int (*less)(void *, void *)) { for(; *lp && less((*lp) -> element, e); lp = &((*lp) -> next)) ; return lp; } static int equal(void *e1, void *e2, int (*less)(void *, void *)) { return !less(e1, e2) && !less(e2, e1); } static int points_to(list *lp, void *e, int (*less)(void *, void *)) { return *lp && equal((*lp) -> element, element, l -> less); } int slset_member(slset l, void *element) { list *lp = find(&(l -> elements), element, l -> less); return points_to(lp, element, l -> less); } void slset_insert(slset l, void *element) { list *lp = find(&(l -> elements), element, l -> less); assert(!points_to(lp, element, l -> less)); *lp = cons(element, *lp); } void slset_delete(slset l, void *element) { list *lp = find(&(l -> elements), element, l -> less); assert(points_to(lp, element, l -> less)); *lp = cdr_and_free(*lp); }Here, we have introduced two new local functions, equal and points_to. Given two elements and a less function, the equal function return a true value if and only if the two elements are equal, i.e. neither one is less than the other. Given a pointer to a list, an element, and a less function, the points_to function returns a true value if and only if dereferencing the pointer gives a cell containing an element equal to the one passed as argument. This becomes exactly the condition to test in the interface functions.