Problem Description:
In the previous exercise we implemented some basic operations on functional-style linked lists; their distinguishing feature is that the pairs that make up the lists are immutable. In today’s exercise we will implement imperative-style linked lists in which the pairs that make up the lists are mutable.
Your task is to write the same library of functions — nil, isNil, pair, head, tail, nth, length, append, and reverse — for imperative-style mutable linked lists as you wrote in the previous exercise for functional-style immutable linked lists.
Analysis:
We will use the well-known imperative language C instead of our usual language Scheme, because writing Scheme like that would make me cry. The basic data type that implements lists is the List, which is a struct; we will hard-wire the data type as int, but you could change that to a pointer to void if you want to be more general:
typedef struct list {
void *data;
struct list *next;
} List;
Instead of providing nil and isNil, we will simply use the built-in NULL for nil and write xs == NULL or xs != NULL to determine if a List is nil. Likewise, getting the components of a pair is so simple we don’t bother to provide functions for them: the head of a list is xs->data and the tail of a list is xs->next.
The insert function adds a new pair to the front of a list by allocating space for the new pair, assigning a value and a pointer to its two fields, and returning the new pair:
List *insert(void *data, List *next) {
List *new;
new = malloc(sizeof(List));
new->data = data;
new->next = next;
return new;
}
Getting the nth item in a list, or counting the length of a list, involves chasing pointers down the list in a while loop. Since C doesn’t have a standard error return, we use our own error function, which writes a message to stderr then exits the program with a return value of 2:
int nth(List *xs, int n) {
while (n > 0) {
if (xs == NULL) {
error(“nth out of bounds”);
}
n–;
xs = xs->next;
}
return xs->data;
}
int length(List *xs) {
int len = 0;
while (xs != NULL)
{
len += 1;
xs = xs->next;
}
return len;
}
Append walks down its first argument, then modifies the NULL pointer at the end of the list to point to its second argument:
List *append(List *xs, List *ys) {
List *new = xs;
while (xs->next != NULL) {
xs = xs->next;
}
xs->next = ys;
return new;
}
Our last function is reverse, which is again destructive, modifying the current list in place by swapping pointers:
List *reverse(List *list) {
List *new = NULL;
List *next;
while (list != NULL)
{
next = list->next;
list->next = new;
new = list;
list = next;
}
return new;
}
We haven’t bothered to collect the garbage we created; shame on me. I admit some trouble programming with C; it’s so ugly compared to Scheme.
Code:
You can see the program in action at http://programmingpraxis.codepad.org/JQIFfqOX.
-from ProgrammingProxis