/** * list.c * A simple implementation of lists. */ // +---------+-------------------------------------------------------- // | Headers | // +---------+ #include #include #include // For strcmp #include "node.h" #include "list.h" #ifdef UNIT_TESTS #include "utest.h" #include "mem.h" #endif #ifdef EXPERIMENTS #include "expt.h" #include "mem.h" #endif // +-------+---------------------------------------------------------- // | Types | // +-------+ struct list { struct node *head; struct node *tail; int length; }; // +---------+-------------------------------------------------------- // | Globals | // +---------+ #ifdef UNIT_TESTS int errors; #endif // +--------------------+--------------------------------------------- // | Exported Variables | // +--------------------+ char *list_error = "unknown error"; // +----------------------------+------------------------------------- // | Constructors & Destructors | // +----------------------------+ struct list * list_new () { struct list *lst = malloc (sizeof (struct list)); if (lst == NULL) return NULL; lst->head = NULL; lst->tail = NULL; lst->length = 0; return lst; } // list_new void list_free (struct list* lst) { // Free all of the cons cells. list_clear (lst); // Reset values (not necessary, but safer). lst->length = 0; lst->head = NULL; lst->tail = NULL; // Free the surrounding struct free (lst); } // list_free void list_clear (struct list *lst) { struct node *garbage; while (lst->head != NULL) { garbage = lst->head; lst->head = cdr (lst->head); free (garbage); } lst->tail = NULL; lst->length = 0; } // list_clear // +-----------+------------------------------------------------------ // | Observers | // +-----------+ char * list_get (struct list *lst, int n) { // Note: If the preconditions are not met, this behaves badly. int i; struct node *current = lst->head; for (i = 0; i < n; i++) current = cdr (current); return car (current); } // list_get int list_length (struct list *lst) { return lst->length; } // list_length int list_index_of (struct list *lst, char *str) { int i; // Andrew likes counter variables // Repeatedly advance the head until you reach the end or ... struct node *current = lst->head; i = 0; while (current != NULL) { // You find what you're looking for if (strcmp (str, car (current)) == 0) return i; // Move on to the next thing current = cdr (current); ++i; } // while // Ran out of things. Must not be there. return -1; } // list_index_of void list_print (struct list *lst, FILE *fp) { np_print (lst->head, fp); } // list_print // +----------+------------------------------------------------------- // | Mutators | // +----------+ int list_set (struct list *lst, int n, char *str) { return 0; // STUB } // list_set int list_insert (struct list *lst, int n, char *str) { return 0; // STUB } // list_insert int list_append (struct list *lst, char *str) { // Build a node to hold the string. struct node *newtail = cons (str, NULL); if (newtail == NULL) return 0; // Special case: List is currently empty if (lst->length == 0) { lst->length = 1; lst->head = newtail; lst->tail = newtail; return 1; } // Normal case: List is nonempty ++(lst->length); set_cdr (lst->tail, newtail); lst->tail = newtail; return 1; } // list_append int list_prepend (struct list *lst, char *str) { struct node *newhead = cons (str, lst->head); if (newhead == NULL) return 0; lst->head = newhead; if (lst->tail == NULL) lst->tail = newhead; ++(lst->length); return 1; } // list_prepend int list_delete (struct list *lst, char *str) { return 0; // STUB } // list_delete int list_cut (struct list *lst, int n) { // Special case: Stupid client if ((n < 0) || (n >= list_length (lst))) return 0; // Special case: Cutting the first element if (n == 0) { struct node *newhead = lst->head->next; free (lst->head); lst->head = newhead; --(lst->length); if (lst->head == NULL) lst->tail = NULL; return 1; } // Advance to the element *before* the nth element struct node *current = lst->head; int i; for (i = 0; i < n-1; i++) current = cdr (current); // Figure out where its next pointer should go struct node *newnext = current->next->next; // Free the element we want to delete free (current->next); // Update the before element's next pointer current->next = newnext; // Update the length --(lst->length); // Special case: Deleted the tail if (n == lst->length) lst->tail = current; // And we're done return 1; } // list_cut // +-----------+------------------------------------------------------ // | Iterators | // +-----------+ void list_foreach (struct list *lst, int (*fun)(char *)) { struct node *current = lst->head; while (current != NULL) { fun (car (current)); current = cdr (current); } // while } // list_foreach // +------------+----------------------------------------------------- // | Unit Tests | // +------------+ #ifdef UNIT_TESTS int main () { struct list *lst1 = list_new (); struct list *lst2 = list_new (); CHECK_INT (list_length (lst1), 0); CHECK_INT (list_length (lst2), 0); // Create a list and make sure that we can delete the first element. // Create a list and make sure that we can delete the last element. // Make sure that it works for a length-one list. // More generally, try lots of lengths and lots of positions. fprintf (stderr, "%d errors encountered.\n", errors); return errors; } // main #endif // UNIT_TESTS // +-------------+---------------------------------------------------- // | Experiments | // +-------------+ #ifdef EXPERIMENTS #define PRINT_LIST(LIST) \ BLOCK( \ fprintf (stderr, VAR_PREFIX "%s = ", #LIST); \ if (LIST == NULL) \ fprintf (stderr, ""); \ else \ list_print (LIST, stderr); \ fprintf (stderr, "\n"); ) #define PRINT_STUFF() \ BLOCK( \ PRINT_LIST (lst1); \ PRINT_LIST (lst2); \ PRINT_INT (mem_allocated ()); \ ) int main () { struct list *lst1, *lst2; char stuff[] = "Hello"; PRINT_INT (mem_allocated ()); EXPERIMENT (lst1 = list_new ()); EXPERIMENT (lst2 = list_new ()); PRINT_STUFF (); EXPERIMENT (list_prepend (lst1, stuff)); PRINT_STUFF (); EXPERIMENT (list_prepend (lst1, stuff)); PRINT_STUFF (); EXPERIMENT (list_prepend (lst1, "Hi")); PRINT_STUFF (); EXPERIMENT (list_prepend (lst2, "Goodbye")); PRINT_STUFF (); EXPERIMENT (list_clear (lst1)); PRINT_STUFF (); EXPERIMENT (list_prepend (lst1, stuff)); PRINT_STUFF (); // Testing list_index_of EXPERIMENT (list_clear (lst1)); EXPERIMENT (list_append (lst1, "Alpha")); EXPERIMENT (list_append (lst1, "Bravo")); EXPERIMENT (list_append (lst1, "Charlie")); EXPERIMENT (list_append (lst1, "Delta")); EXPERIMENT (list_append (lst1, "Echo")); EXPERIMENT (list_append (lst1, "Foxtrot")); PRINT_STUFF (); PRINT_INT (list_index_of (lst1, "Alpha")); PRINT_INT (list_index_of (lst1, "Beta")); PRINT_INT (list_index_of (lst1, "Bravo")); PRINT_INT (list_index_of (lst1, "Charlie")); PRINT_INT (list_index_of (lst1, "Delta")); PRINT_INT (list_index_of (lst1, "Foxtrot")); // Testing list_cut EXPERIMENT (list_cut (lst1, 0)); PRINT_STUFF (); EXPERIMENT (list_cut (lst1, list_length (lst1) - 1)); PRINT_STUFF (); EXPERIMENT (list_cut (lst1, 2)); PRINT_STUFF (); EXPERIMENT (list_clear (lst2)); EXPERIMENT (list_prepend (lst2, "One Element")); PRINT_STUFF (); EXPERIMENT (list_cut (lst2, 0)); PRINT_STUFF (); // Create a list and make sure that we can delete the first element. // Create a list and make sure that we can delete the last element. // Make sure that it works for a length-one list. EXPERIMENT (list_free (lst1)); EXPERIMENT (list_free (lst2)); PRINT_INT (mem_allocated ()); return 0; } // main #endif // EXPERIMENTS