Laboratory: Numeric RecursionSummary:
Although most of our prior experiments with
recursion have emphasized recursion over lists, it is also possible
to use other values as the basis of recursion. In this laboratory,
you will explore the use of natural numbers (non-negative integers)
as the basis of recursion.
Reference
Here is a template for the simplest kind of numeric recursive
procedures.
(define recursive-proc
(lambda (n)
(if (zero? n)
base-case
(combinen (recursive-proc (- n 1))))))
If you choose to use a local kernel, here is one common form.
(define recursive-proc
(letrec ((kernel (lambda (so-far n)
(if (zero? n)
so-far
(kernel (update so-far n)
(- n 1))))))
(lambda (n)
(kernel starting-value n))))
We can also use a kernel to count up from 1 to n.
(A slight change lets you count up from 0 to n.)
(define recursive-proc
(letrec ((kernel (lambda (so-far i n)
(if (> i n)
so-far
(kernel (update so-far i) (+ i 1) n)))))
(lambda (n)
(kernel starting-value 1 n))))
Preparation
Make a copy of numeric-recursion-lab.scm, which contains important code from the reading.
ExercisesExercise 1: Termial, Revisited
Rewrite termial it so that the kernel is local.
Exercise 2: Counting Down
Define and test a recursive Scheme
procedure, (count-downval), that takes a natural number as
argument and returns a list of all the natural numbers less than or
equal to that number, in descending order.
>(count-down 5)(5 4 3 2 1 0)>(count-down 0)(0)
Note that you should use cons to build up the list.
Note also that you are better off writing this with direct recursion
(the first pattern above), rather than using a helper procedure.
When you are finished, you may want to read the notes on this
exercise.
Exercise 3: Filling Lists
Define and test a Scheme procedure,
(my-make-listcountvalue),
that takes two arguments, the
first of which is a natural number, and returns a list consisting of
the specified number of repetitions of the second argument.
>(my-make-list 5 "sample")("sample" "sample" "sample" "sample" "sample")>(my-make-list 3 10)(10 10 10)>(my-make-list 1 null)(())>(my-make-list 2 null)(() ())>(my-make-list 0 "hello")()
You should not call the built-in make-list
procedure. Instead, implement my-make-list recursively.
When you are finished writing this procedure, compare it to
the notes on
this exercise.
Exercise 4: Counting To
As you may recall, iota is a procedure that takes
a natural number as argument and returns a list of all the natural
numbers that are strictly less than the argument, in ascending order.
As you've seen, the iota procedure is quite
useful. Unfortunately, it does not come as part of standard Scheme.
(We include it in MediaScheme because it is so useful.)
Implement and test your own version of iota.
(You should not call the MediaScheme iota.)
For example,
>(my-iota 3)(0 1 2)>(my-iota 5)(0 1 2 3 4)>(my-iota 1)(0)
Note that you will probably need to use a helper of some sort to
write my-iota. You might use the traditional form
of helper, which adds an extra parameter. You might also use a helper
that simply computes the list in the reverse order. (Most students write
a backwards version in the first attempt; instead of throwing it away,
rename it and call it from my-iota.)
Exercise 5: Counting Between
You may recall the count-from procedure from
the
reading on recursion
over natural numbers, which is available in the code
for this lab.
a. What is the value of the call (count-from -10 10)?
b. Check your answer experimentally.
c. It is possible to implement count-from in terms
of iota. The implementation looks something like
the following.
(define count-from
(lambda (lower upper)
(map (lambda (n) (+ _____ n)) (iota _____))))
Finish this definition.
d. What do you see as the advantages and disadvantages of each way of
defining count-from?
For Those Who Finish EarlyExtra 1: The Nth Element
Write a procedure, (my-list-reflstn), that
extracts element n of a list. For example,
>(my-list-ref (list "red" "orange" "yellow" "green" "blue" "indigo" "violet") 5)"indigo">(my-list-ref (list "red" "orange" "yellow" "green" "blue" "indigo" "violet") 0)"red"
Even though this procedure does the same thing as
list-ref, you should not use
list-ref to implement it. Instead, your goal
is to figure out how list-ref works, which means
that you will need to implement this procedure using direct recursion.
Hint: When recursing, you will need to simplify the numeric
parameter (probably by subtracting 1) and the list parameter (probably
by taking its cdr).Exercise 2: Computing List Prefixes
a. Define and test a recursive procedure, (list-prefix
lstn), that
returns a list consisting of the first n elements
of the list, lst, in their original order.
You might also think of list-prefix as returning
all the values that appear before index n.
For example,
>(list-prefix (list "a" "b" "c" "d" "e") 3)("a" "b" "c")>(list-prefix (list 2 3 5 7 9 11 13 17) 2)(2 3)>(list-prefix (list "here" "are" "some" "words") 0)()>(list-prefix (list null null) 2)(() ())>(map rgb->color-name (list-prefix (map color-name->rgb (list "black" "white" "green") 1))("black")
While your procedure has much the same purpose as
list-take, you should not call
list-take. Rather, your goal is to implement
the procedure recursively.
b. Update your procedure to check appropriate preconditions. In particular,
your procedure should signal an error if lst
is not a list, if n is not an exact integer,
if n is negative, or if n
is greater than the length of lst.
Note that in order to signal such errors, you may want to take advantage
of the husk-and-kernel programming style. You can use named let or
letrec.
c. Compare your error messages for various invalid parameters with
those given by list-take.
Extra 3: Taking Some More Elements
Rewrite list-prefix to use whichever of named
let and letrec you didn't
use in the previous exercise.
NotesNotes on Exercise 2: Counting Down
Here's a possible solution to the problem. The base case is easy. If
the number is zero, then the list of all non-negative numbers less than or
equal to zero is the list that contains only zero.
(if (zero? n)
(list 0)
In the recursive case, we assume that we can compute the list of all
numbers less than or equal to n-1. To get the
complete list, we simply add n to the front.
(cons n (count-down (- n 1)))
Putting it all together, we get
Return to the problem.
Notes on Exercise 3: Filling Lists
We begin by considering the base case. The last example gives a hint: If
you want zero copies, you end up with the empty list.
(if (zero? n)
null
Now, on to the recursive case. If we can create a list of
n-1 copies, we can then create a list of
n copies by prepending one more copy.
(cons val (value-replicate val (- n 1)))
Putting it all together, we get
Return to the problem.