Recursive Data Types

Chapter: Recursive Data Types

Q. 1
What is a list?


That's a recursive definition. A list is defined either by a base case empty or by a recursive case. The recursive case tells you how to make a bigger list from an existing list. The base case tells you how to make the smallest list. It's rather like using the principle of mathematical induction to prove theorems: Establish the truth of the theorem for an initial case (typically 0, if working with integers), and then show how to deduce the truth of the theorem for a large case using the established truth of the theorem in previous cases.

Recursive definitions of data structures lead to very elegant implementations and encourage the use of correctness proving via induction. Thus they accord with some of the most sound software engineering principles. Here for example is how the recursive definition of a list leads to a simple implementation of lists of ints:

struct iNode {
  int myInt;                /* the data */
  struct iNode *next; /* the rest of the list (itself, of course, a list)
} 

Note that it would be illegal, in C, for a struct to contain an instance of itself. But iNode contains a pointer to an iNode, not the iNode itself.

In order to declare an iNode you will need code like:

  struct inode *temp;

Notice how we prefer to have pointers to dynamic, recursively defined, structures. Study the following program:

#include  < stdio.h >
void extern exit(int);

/* Read all the ints in a file, sort them and count each 
   Rhys Price Jones 19 Sep 2005
   This code is to be improved by introducing a typedef
   so that all references to 'struct inode' can be replaced by IntNode
   and all references to 'struct inode *' can be replaced by IntNodePtr 
*/ 
 
FILE *inFile;

int this;
int done = 0;

/* We'll store the ints and their counts in a sorted linked list of ints: */

struct inode {
  int data;         /* the int */
  int count;        /* how often it occurred */
  struct inode *next;
};

int digit(char ch) {
  return  (ch > = '0'&&ch < = '9');
}

int getint() {
  if (done) return (int) EOF;
  char *ch;
  char buffer[20];
  ch = buffer;
  while (!digit(*ch = fgetc(inFile)) && (*ch != EOF));
  if (*ch == EOF) return (int) EOF;
  while (digit(*++ch = (fgetc(inFile))));
  if (*ch == EOF) done = 1;
  *ch++ = '\0';
  return atoi(buffer);
}

/* to output our data */
void print_list(il) 
  struct inode *il;
{
  if (il == NULL) printf("\n");
  else {
    printf("%d appears %d times\n", il->data, il->count);
    print_list(il->next);
  }
}

/* to insert into a sorted linked list */
struct inode *insert(il, i) 
     struct inode *il;
     int i;
{
  struct inode *temp;
  struct inode *malloc(int);

  // printf("Inserting %d\n", i);
  if (il == NULL) {    /* insert into empty list */
    il = malloc(sizeof(struct inode));
    il->data = i;
    il->count = 1;
    il->next = NULL;
  }
  else if (i == il->data)
    il->count++;
  else if (i < il->data) {
    temp = il;
    il = malloc(sizeof(struct inode));
    il->data = i;
    il ->count = 1;
    il -> next = temp;
  }
  else il->next = insert(il->next, i);
  // print_list(il);
  return il;
}


void recycle(struct inode *ip) {
  struct inode *np;
  if (ip == NULL); // there is nothing to do
  else {
    np = ip->next;
    free(ip);
    ip = np;
  }
}

main(argc, argv)
     int argc; char *argv[];
{
  struct inode *root;
  if (argc < 1) {
    fprintf(stderr, "Usage: fread  < filename > \n");
    exit(-1);
  }
  inFile = fopen(argv[1], "r");
  int this;
  root = NULL;
  while ((this = getint()) != (int) EOF) root = insert(root, this);
  print_list(root);
  recycle(root);
}

Hardened C programmers may be comfortable with lines of code like

struct inode *insert(struct inode *il, int i) 
but many people would like to replace such lines with
IntNodePtr insert(IntNodePtr il, int i)
or even,
IntList insert(IntList il, int i)

You can achieve this by using typedef. For example

typedef struct inode {
  int data;     /* my word is a pointer to the word */
  int count;        /* how often it occurred */
  struct inode *next;
} IntNode, *IntNodePtr;
allows you to replace every occurrence of struct inode in the original program by IntNode, and every occurrence of struct inode * by IntNodePtr. Try it, and see if you agree that the readability is improved. The next exercise asks you to go further.


Exercise 1

Use typedef to improve the readability of the code given above. The resulting program should work exactly like the original. But it should be much more readable and much more understandable to a normal human being who knows a little C but enjoys elegance in coding.


rhyspj@gwu.edu