a. Discuss the self check with your partner.
b. Do the normal lab setup. That is
loudhum
package is up to date.(require loudhum)
to the top of the definitions pane.c. Make a copy of the annotated new-sum-helper
procedure from the reading,
as well as new-sum
and any accompanying documentation.
d. Verify that new-sum-helper
does, in fact, print the expected
information about intermediate procedure calls.
a. Add the following code (taken from the reading) to your definitions pane.
;;; Procedure:
;;; furthest-from-zero
;;; Parameters:
;;; numbers, a nonempty list of real numbers
;;; Purpose:
;;; Find the number in the list with the largest absolute value.
;;; Produces:
;;; furthest, a real number
;;; Preconditions:
;;; [No additional]
;;; Postconditions:
;;; furthest is an element of numbers
;;; For any number, n, in numbers
;;; (>= (abs furthest) (abs n))
(define furthest-from-zero
(lambda (numbers)
(furthest-from-zero-helper (car numbers) (cdr numbers))))
(define furthest-from-zero-helper
(lambda (furthest-so-far numbers-remaining)
(if (null? numbers-remaining)
furthest-so-far
(furthest-from-zero-helper
(further-from-zero furthest-so-far (car numbers-remaining))
(cdr numbers-remaining)))))
;;; Procedure:
;;; further-from-zero
;;; Parameters:
;;; val1, a real number
;;; val2, a real number
;;; Purpose:
;;; Choose whichever of val1 and val2 is further from zero.
;;; Produces:
;;; further, a real number
;;; Preconditions:
;;; [No additional]
;;; Postconditions:
;;; further is either val1 or val2
;;; (abs further) >= (abs val1)
;;; (abs further) >= (abs val2)
(define further-from-zero
(lambda (val1 val2)
(if (>= (abs val1) (abs val2))
val1
val2)))
b. Add appropriate calls to display
and newline
so that
you can track all the calls to furthest-from-zero-helper
and
further-from-zero
.
c. Sketch (that is, compute by hand) the output you expect to see in
call to (furthest-from-zero (list -3 5 6 -2 1 -5))
. Your
sketch might start something like the following.
(furthest '(-3 5 6 -2 1 -5))
=> (helper -3 '(5 6 -2 1 -5))
=> (helper (further '-3 5) '(6 -2 1 -5))
=> (helper 5 '(6 -2 1 -5))
...
d. Check your answer by asking DrRacket to evaluate that expression.
a. Rewrite the product
procedure, which computes the product of a list
of values, using the same technique used for new-sum
.
b. Write a similar my-quotient
procedure. (Do not call it quotient
,
which is a built-in procedure that is commonly used.)
For example,
> (my-quotient (list 3))
3
> (my-quotient (list 3 5))
3/5
> (my-quotient (list 3 5 7))
3/35
> (my-quotient (list 3 5 7 6))
1/70
> (my-quotient (list 3 5 7 6 1/10))
1/7
Do not use product
as a helper.
You may recall that in the previous lab, you
wrote a procedure, tally-numbers
, that took as input a list of mixed
values (both numbers and non-numbers) and returned a count of the number
of numbers in the list.
We will be rewriting tally-numbers
using helper recursion. The helper
procedure will likely have two parameters: a count of all the numbers
you’ve seen so far and a list of all the values you have left to look at.
a. Make a table with two column labels: count-so-far
and remaining
.
Here’s what we might expect those columns to have after we’ve done
the first few steps of the helper on the list '(5 a b 1 c 2 3 b 4)
tally-so-far remaining
------------ ---------
0 '(5 a b 1 c 2 3 b 4)
1 '(a b 1 c 2 3 b 4)
1 '(b 1 c 2 3 b 4)
1 '(1 c 2 3 b 4)
2 '(c 2 3 b 4)
Fill in the next rows to suggest what might/should happen.
b. Using the ideas you gained in those steps, implement tally-numbers
and tally-numbers-helper
.
c. Check your answers on some of the following lists
> (tally-numbers null)
> (tally-numbers (list 1 2 3))
> (tally-numbers (list "a" "b" "c"))
> (tally-numbers (list 1 "a" 2 "b" 3 "c"))
d. Update tally-numbers-helper
to print out calls, and call it
on the list '(5 a b 1 c 2 3 b 4)
to see if it matches your
answer to part a.
You may recall that in the reading on recursion
basics, we wrote a procedure,
select-numbers
, that, given a list of values, returns a list with
just the numbers from the original list.
Here’s an attempt to implement that procedure using helper recursion.
;;; Procedure:
;;; select-numbers
;;; Parameters:
;;; values, a list of Scheme values
;;; Purpose:
;;; Create a new list values which consists of only the numbers
;;; in values.
;;; Produces:
;;; nums, a list of Scheme values
;;; Preconditions:
;;; [No additional]
;;; Postconditions:
;;; * number? holds for every value in nums.
;;; * Every element of values for which number? holds appears in nums.
;;; * Every element of nums appears in values.
;;; * If a number appears multiple times in either list, it appears
;;; the same number of times in both lists.
;;; * The values retain their order.
(define select-numbers
(lambda (values)
(select-numbers-helper null values)))
(define select-numbers-helper
(lambda (nums-so-far remaining)
; (display (list 'select-numbers-helper nums-so-far remaining)) (newline)
(cond
[(null? remaining)
nums-so-far]
[(not (number? (car remaining)))
(select-numbers-helper nums-so-far (cdr remaining))]
[else
(select-numbers-helper (cons (car remaining) nums-so-far)
(cdr remaining))])))
a. Add the code and documentation to your definitions pane.
b. As you may have observed in the previous problem, it can be helpful to understand tail recursive procedures by creating a table whose columns are labeled with the parameters and which each row gives the values of those parameters in one call.
Make a table with two column labels: num-so-far
and remaining
.
Here’s what we might expect those columns to have after we’ve done
the first few steps of the helper on the list '(a b 1 c 2 3 b 4)
num-so-far remaining
---------- ---------
'() '(a b 1 c 2 3 b 4)
'() '(b 1 c 2 3 b 4)
'() '(1 c 2 3 b 4)
'(1) '(c 2 3 b 4)
Fill in the remaining five or so rows.
c. What output do you expect to get from
(select-numbers '(a b 1 c 2 3 b 4))
?
d. Check your answer experimentally.
e. If you did not get the answer you expected, uncomment the line that logs procedure calls and run it again.
As you may have noted, the prior implementation of select-numbers
produces the list of values in reverse order that they appear in the
original list. Rewrite the procedure so that it produces the list of
values in the same order as they appear in the original list. You
may not use append
in that implementation.
You may recall that we claimed that one of the implementations of
furthest-from-zero
using direct recursion was incredibly expensive.
Let’s check that claim.
a. Add the following definition of furthest-from-zero
to your
definitions pane.
(define furthest-from-zero-alt
(lambda (numbers)
(display (list 'furthest-from-zero-alt numbers)) (newline)
(if (null? (cdr numbers))
(car numbers)
(if (>= (abs (car numbers))
(abs (furthest-from-zero-alt (cdr numbers))))
(car numbers)
(furthest-from-zero-alt (cdr numbers))))))
b. What output do you expect from the following procedure call? (Detail both the answer and the log messages.)
> (furthest-from-zero-alt (list 8 5 3 1 0))
c. Check your answer experimentally.
d. What output do you expect from the following procedure call? (Detail both the answer and the log messages.)
> (furthest-from-zero-alt (list 0 1 3 5 8))
e. Check your answer experimentally.
f. Explain, in your own words, why one of the two results looks so much worse.
g. How many calls to furthest-from-zero-alt
do you expect to see
if we ask for the values of (furthest-from-zero-alt (iota 8))
?
h. Check your answer experimentally.
As we noted in the reading the problem with furthest-from-zero-alt
is that we may have two recursive calls each time, rather than one.
That’s dangerous. We suggested a few alternative strategies in the
reading. Let’s consider another one. Suppose we use let
to bind
names to the car
and the result of of the recursive call.
(let ([candidate (furthest-from-zero-alt (cdr numbers))]
[alternate (car numbers)])
a. Rewrite furthest-from-zero-alt
to use these names in place of the
values they name.
b. What effect do you expect this naming to have on the efficiency of the procedure?
c. Check your answer experimentally.
The following exercises will challenge you to extend the problem-solving strategies you’ve learned so far.
Write a procedure, tally-numbers-strings-and-symbols
, that takes
as input a mixed list and produces a list of tallys of the number
of numbers, strings, and values.
> (tally-numbers-strings-and-symbols null)
'(0 0 0)
> (tally-numbers-strings-and-symbols '(a 1 2 b "c" 3))
'(3 2 1)
> (tally-numbers-strings-and-symbols '(a b "c" "3" "e"))
'(0 2 3)
You will find it useful to base tally-numbers-strings-and-symbols
on
the helper-recursive tally-numbers
your wrote earlier in this lab.
Rewrite the original furthest-from-zero
so that the primary procedure
(that is, furthest-from-zero
) checks all of its preconditions before
calling the helper.
The reading provides at least four ways to find the number furthest from zero in a list of numbers. You’ve developed at least one more in this lab. Which do you prefer, and why?