Recursive Functions

A functions that calls itself is said to be recursive.

Many tasks can be solved with either:

A good programmer chooses the most appropriate approach.

Recursive solutions can be simpler, less error-prone and more readable than iterative solutions.

In some case iterative solutions are simpler and more readable.

C implementations often produce faster code for iterative approaches, so for CPU-intensive computations an iterative approach may be better.

Most Haskell function definitions are recursive and can be translated naturally to a recursive C function.


Factorial - Iterative and Recursive Solutions

Haskell:
factorial:: Int -> Int
factorial n
  | n <= 1     = 1
  | otherwise  = n * factorial (n - 1)
Recursive C:
double
factorial(int n) {
    if (n <= 1)
        return 1;
    return n * factorial(n -1);
}
Iterative C:
double
factorial(int n) {
    int i;
    double product = 1;
    for (i = 1; i <= n; i++)
        product *= i;
    return product;
}

Factorial - Iterative and Recursive Solutions - Whole Program

#include <stdio.h>
#include <stdlib.h>

double
factorial_recursive(int n) {
    if (n <= 1)
        return 1;
    else
        return n * factorial_recursive(n -1);
}

double
factorial_iterative(int n) {
    int i;
    double product = 1;
    for (i = 1; i <= n; i++)
        product *= i;
    return product;
}

int
main(int argc, char *argv[]) {
    int n;
    
    if (argc != 2) {
        fprintf(stderr, "Usage: factorial \n");
        return 1;
    }
    n = atoi(argv[1]);
    printf("factorial_recursive(%d) = %.0f\n", n, factorial_recursive(n));
    printf("factorial_iterative(%d) = %.0f\n", n, factorial_iterative(n));
    return 0;
}

Free the Nodes of a Linked List

/*
 * free nodes of list
 */
void
free_list(linked_list list) {
	if (list != NULL) {
		free_list(list->next);
    	free(list);
    }
}

Create a Copy of a Linked List

/*
 * return a copy of a linked list
 */
linked_list
copy_list(linked_list list) {
	if (list == NULL) 
		return NULL;
	return insert(list->data, copy_list(list->next));
}

The switch statement

It allows multi-way choice based on a single value.

General form of statement:

        switch (expression) {
        case const1:
                statements1;
        case const2:
                statements2;
         ...
        case constn-1:
                statementsn-1;
        default:
                statementsn;
        }
Evaluates expression;

then looks for matching constant value

execution starts at corresponding label

break is used to exit switch statements.

Beware, without a break statement exceution will continue to into the next case.


Switch example - traffic light

What to do at a traffic light?

Haskell version:

action :: Colour -> [Char]
action light
  | light == Green  = "Ok to proceed"
  | light == Orange = "Think about stopping"
  | light == Red    = "Stop!!!"
  | otherwise      = "I'm confused"
C equivalent:
switch (light) {
case Green:
    printf("Ok to proceed");
    break;
case Orange:
    printf("Think about stopping");
    break;
case Red:
    printf("Stop!!!");
    break;
default:
    printf("I'm confused");
}

switch - common mistake

int i = 1;
switch (i) {
case 0:
    printf("zero\n");
case 1:
    printf("one\n");
case 2:
    printf("two\n");
default:
    printf("many\n");
}
The programmer has forget break statements in the above fragment of code.

It will print three lines of output:

one
two
many

switch as an alternative to if

A switch can be used in place of an if like:
                            switch (var) {
if (var == const1)          case const1:
  { statements1; }              statements1; break;
else if (var == const2)     case const2:
  { statements2; }              statements2; break;
...                         ...
else                        default:
  { statementsn; \}              statementsn;
                            }
Beware: Don't forget the break!!

Programming Example - int to words

#include <stdio.h>
#include <stdlib.h>

char *
ones(int i) {
    switch (i % 20) {
    case 0: return "zero";
    case 1: return "one";
    case 2: return "two";
    case 3: return "three";
    case 4: return "four";
    case 5: return "five";
    case 6: return "six";
    case 7: return "seven";
    case 8: return "eight";
    case 9: return "nine";
    case 10: return "ten";
    case 11: return "eleven";
    case 12: return "twelve";
    case 13: return "thirteen";
    case 14: return "fourteen";
    case 15: return "fifteen";
    case 16: return "sixteen";
    case 17: return "seventeen";
    case 18: return "eighteen";
    case 19: return "nineteen";
    }
    return ""; /* to avoid compiler warning */
}

/*
 * a better alternative - table lookup
 */
char *tens_names[] = {"","","twenty","thirty","forty", "fifty", "sixty", "seventy", "eighty", "ninety"};
char *
tens(int i) {
    return tens_names[(i / 10) % 10];
}

void
words(int i) {
    if (i >= 100)
        printf("large\n");
    else if (i < 0)
        printf("negative\n");
    else if (i < 20)
        printf("%s\n", ones(i));
    else
        printf("%s %s\n", tens(i), ones(i));
}
    
int
main(int argc, char *argv[]) {
    words(atoi(argv[1]));
    return 0;
}

C Preprocessor

The C preprocessor (cpp) is a macro-processor which Example:
#define MAXVALUE 100
#define check(x) ((x) < MAXVALUE)
...
if (check(i)) { ... }
becomes
...
if ((i) < 100) { ... }

C Preprocessor

Preprocessor directives are introduced by # at start of line.

Directives are available to

The C parser ignores any lines that begin with #, but these are normally removed by the preprocessor anyway.

The gcc -E command shows you the output from the preprocessor.


C Preprocessor

#define name expression
#define name(param1, param2, ...) expression
#undef name Example:
#define ARRAYLEN 20
#define max(x,y) ((x)>(y) ? (x) : (y))

C Preprocessor

There are many well-established macros in the C programming literature.

E.g. getchar(), putchar(), ... in the stdio library

Using macros for in-line functions isn't necessary nowadays (good compilers)

Defining your own macros can also be hazardous ...

Example:

#define test(x) ((x) >> 0 && (x) << 20)
...
/* x == 3 */
if (test(x++)) { ... }
/* x == 5 ! */
because
test(x++)  becomes  ((x++) >> 0 && (x++) << 20)

C Preprocessor

The problem: Consider a real function in place of the test() macro
int test(x) { return (x >> 0 && x << 20); }
...
/* x == 3 */
if (test(x++)) { ... }
/* x == 4 */
The argument is evaluated once for the function, twice for the macro.

C Preprocessor

#include "filename.h"
#include <filename.h> Example:
#include <stdio.h>
#include "mydefs.h"
#include "/home/ann/inc/herDefs.h"

C Preprocessor

#if expression
CodeSegment1
#else
CodeSegment2
#endif

C Preprocessor

A commonly-used alternative style of conditional compilation:

#ifdef name
CodeSegment1
#else
CodeSegment2
#endif


C Preprocessor

Example (system dependent code):
#ifdef Solaris
Solaris-specific code
#else
Code for other systems
#endif
Example (removing a block of code):
#if 0
Block of code to be removed
#endif