There are two ways to define a list containing multiple forms of information. Let's consider two examples. First, list-of-nums-and-syms. Here are the two alternatives: Definition 1: A list-of-nums-and-syms is one of - empty, - (cons S lons) where S is a symbol and lons is a list-of-nums-and-syms, or - (cons N lons) where N is a number and lons is a list-of-nums-and-syms. Definition 2: A list-of-nums-and-syms is either - empty, or - (cons NS lons) where NS is a num-or-sym and lons is a list-of-nums-and-syms. A num-or-sym is either - a number, or - a symbol. Second, a list of pizza toppings. Here are the two alternatives: Definition 1: A list-of-toppings is one of - empty, - (cons 'cheese alot), where alot is a list-of-toppings, - (cons 'ham alot), where alot is a list-of-toppings, or - (cons 'onion alot), where alot is a list-of-toppings. Definition 2: A list-of-toppings is either - empty, or - (cons T alot), where T is a topping and alot is a list-of-toppings. A topping is one of - 'cheese, - 'ham, or - 'onion. Which style is better, 1 or 2? You will gain much more insight into this question as you go on in computer science. For now, here's a general rule. If there is a meaningful relationship between the cases (as in the toppings example), use style 2. Otherwise, you can use style 1. Note that in practice, the former case occurs far more often than the latter. You won't go wrong if you always use style 2. Natural Numbers Write a program gos which consumes a natural number n and returns a list with n copies of 'go. What's wrong here? This isn't a simple function that computes one number from another. It's more complicated than that. But for more complicated programs, we usually start with a data definition. Where's the data definition? Let's write a data definition for the natural numbers. When we wrote the data definition for lists, we started with the smallest possible list, empty. What's the smallest natural number? 0. How did we build up lists? We added one element onto an existing list. Using the same idea, how do we build up the natural numbers? We add one to an existing number. This yields the following data definition: A natural number is either - 0, or - (add1 n) where n is a natural number Let's define some natural numbers using this definition: 0 (add1 0) (add1 (add1 0)) (add1 (add1 (add1 0))) Recall that in the case of lists, we have cons to build lists up and first and rest to take lists apart. We can build up natural numbers using add1. How do we take them apart? We use sub1. Now that we have the data definition, let's write gos. ; gos : N -> list-of-symbols ; create a list with n copies of 'go (define (gos n) ...) What are some examples? (gos 0) = empty (gos 2) = (cons 'go (cons 'go empty)) What does the template look like? (define (gos n) (cond [(zero? n) ...] [else ... (gos (sub1 n)) ...])) Here's the full function. (define (gos n) (cond [(zero? n) empty] [else (cons 'go (gos (sub1 n)))])) Admittedly, this is a somewhat silly example. Let's turn to something more realistic. Sometimes, we want to graph functions over the natural numbers. Let's write a program create-points that we could use to graph the function f(x) = x * x. What do we need? A data definition for points. A point is a structure (make-point x y) where x and y are numbers ;; create-points : N -> list-of-points ;; creates a list of points in which each y coordinate is the square ;; of the x coordinate (define (create-points n) (cond [(zero? n) empty] [else (cons (make-point n (* n n)) (create-points (sub1 n)))])) This program produces points for numbers in the range 0 to n. What if we wanted our program to only graph numbers larger than 10? We need to change the purpose statement and we need a new data definition. A natural number [>= 10] is either - 10, or - (add1 n), where n is a natural number [>= 10] How does the function above change? Only in the contract and the base case. ;; create-points : N [>= 10] -> list-of-points ;; creates a list of points in which each y coordinate is the square ;; of the x coordinate and each x coordinate is larger than 10. (define (create-points n) (cond [(= n 10) empty] [else (cons (make-point n (* n n)) (create-points (sub1 n)))])) What if we wanted to include the point with x-coordinate 10? How do we change the above function? ;; create-points : N [>= 10] -> list-of-points ;; creates a list of points in which each y coordinate is the square ;; of the x coordinate and each x coordinate is larger than 10. (define (create-points n) (cond [(= n 10) (cons (make-point n (* n n)) empty)] [else (cons (make-point n (* n n)) (create-points (sub1 n)))])) Why don't we just change (= n 10) to (< n 10)? Because that would violate the data definition. Finally, let's develop a program multiply, which consumes a natural number n and a number m and produces n * m, but without using multiplication. ;; multiply : N num -> num ;; multiplies a natural number by a num without using * (define (multiply n m) (cond [(zero? n) 0] [else (+ m (multiply (sub1 n) m))]))