When writing recursive functions, pay attention to the type of what you are recurring on, as well as to the type of what you want your result to be. That information alone can help you determine the pieces to use in writing the function. As an example, consider the following simple recursive design pattern.

(define (fn arg) (if (basecase? arg) basecaseanswer (combine (f arg) (fn (smaller arg)))))

Here are four examples of functions that follow this design pattern. Notice what they take as input and output.

;;; takes a number n as input and returns a number that is equal to n! (define (fact n) (if (zero? n) 1 (* n (fact (1- n))))) ;;; takes a number as input and returns a list of numbers ;;; that looks like a shuttle launching countdown (define (make-lst n) (if (zero? n) (list 'blastoff) (cons n (make-lst (1- n)))))

;;; take a list as input and returns a number equal to the length of the list (define (count lst) (if (null? lst) 0 (+ 1 (count (cdr lst))))) ;;; takes a list as input and returns a list which is just a ;;; copy of the original list (define (copy lst) (if (null? lst) '() (cons (car lst) (copy (cdr lst)))))

Just from the type of the input, you can narrow down the function that
you use to determine whether or not you've hit the basecase to
predicates that recognize objects of that same type. The input type
call also help you determine what is often called the *natural
recursion* of the function, which is how to break down the function
into its recursive call.

fn | type of arg | basecase? | smaller |
f |

fact | number | zero? | 1- | identity |

make-lst | number | zero? | 1- | identity |

count | list | null? | cdr | car |

copy | list | null? | cdr | car |

Note that the function ` f` in the design pattern is some arbitrary
function, however if it is the identity function instead of writing
` (identity arg)` in the code we just write ` arg`.

For functions that take numbers as input, the functions `
basecase?` and ` smaller` are functions that act on numbers, and
similarly with lists. There are often not so many functions that
recognize any one particular type, and not so many ways to recurse on
any one data type, so this drastically reduces your options to choose
from for those functions.

The type of the output of the function can also give you some clues about the other components of your recursive funtion.

fn | type of output | combine |
basecaseanswer |

fact | number | * | 1 |

count | number | + | 0 |

make-lst | list | cons | '(blastoff) |

copy | list | cons | '() |

Usually basecaseanswer tends to be a constant, although in general it
might actually depend on ` arg`. When returning numbers, you
usually use 0 as the basecaseanswer and ` +` as the combining
function or 1 and ` *`. When returning lists, the basecaseanswer
is frequently the empty list and the combining function is almost
always ` cons`.

Again, notice that the type we want our output to be determines not only the type of the basecase answer, but also severely limits our options of functions to use to combine the result of the recursive call with some local value.

Of course, this generalizes to other design patterns as well. As an exercise, see what information you can glean from the following two functions, one which has more than one input argument but still uses a design pattern similar to the one given here, and one which uses the tail-recursive design pattern.

;;; takes two lists as input and returns a list equal to the concatenation ;;; of the two lists. It recurs over the first list, making a copy of it, ;;; but instead of the empty list at the end it puts the (actual) second list (define (append a b) (if (null? a) b (cons (car a) (append (cdr a) b))))

;;; takes a list as input, returns a list that is the reverse of the original (define (reverse x) (reverse-h x '())) (define (reverse-h x ans) (if (null? x) ans (reverse-h (cdr x) (cons (car x) ans))))