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.