Do the normal preparation for a lab. That is,
/home/rebelsky/bin/csc151/update
.loudhum
package with (require loudhum)
.Consider a procedure, (greatest-of-list lst)
, that finds the largest
number in a list.
(define greatest-of-list
(lambda (lst)
(reduce max lst)))
a. What preconditions should greatest-of-list
have?
b. What postconditions should greatest-of-list
have?
c. Discuss your answers with a neighboring group. (Be prepared to discuss them with an instructor or mentor, too.)
In the lab on list processing, you saw
that you could compute the average of a list using a combination
of reduce
and length
.
a. Write a procedure, (average lst)
, that averages the values
in lst
.
b. What preconditions should average
have?
c. How are those preconditions similar to those of greatest-of-list
?
How do they differ?
d. Discuss your answers with a neighboring group. (Be prepared to discuss them with an instructor or mentor, too.)
In thinking about the procedures that work on lists, such as map
,
reduce
, tally
, or sort
, we often use the mantra “First solve for
one or two elements, then solve for many”. That is, because each
of these procedures takes a list and a procedure, our first focus
should be on how that procedure works with individual elements or
pairs of elements.
map
, we most frequently work with single elements, so
we make sure our one-parameter procedure works and then map
it over the list.reduce
, we need a procedure that combines neighboring
elements, so we develop a two-parameter procedure, check
it on pairs of elements, and then use reduce
to combine
all of the elements in the list.tally
and filter
, we need a predicate that looks at a single
value and returns true or false.sort
, we need a predicate that looks at two values and returns
true if the first should come before the second.a. Suppose we have a list of strings and want to duplicate every string in the list.
> (map ??? '("a" "b" "c"))
'("aa" "bb" "cc")
What procedure should you use? (You may have to define it yourself.)
b. Suppose we want to combine all of the strings in a list with
spaces between them. What procedure should you use along with
reduce
? (Once again, you may have to define it yourself.)
> (reduce ??? '("a" "b" "c"))
"a b c"
c. Suppose we want to sort a list of strings in order of
increasing length. What procedure should you use along with
sort
? (Once again, you may have to define it yourself.)
> (sort '("longish" "short?" "a" "hello" "two") ???)
'("a" "two" "hello" "short?" "longish")
It seems that determining whether all of the elements of a list are
a particular type, such as all integers or all strings, should use
such an approach. It may feel like like we should be able to map
a predicate onto the list to get a list of #t
and #f
and then
reduce using and
. Let’s try.
> (define reals (list 1 3.2 -5 8 23))
> (define odds (list 1 3 5 7 5 1 7 1))
> (define mixed (list "a" 23 'c 11))
> (map real? reals)
'(#t #t #t #t #t)
> (map real? odds)
'(#t #t #t #t #t #t #t #t)
> (map real? mixed)
'(#f #t #f #t)
> (map (conjoin integer? odd?) reals)
'(#t #f #t #f #t)
> (map (conjoin integer? odd?) odds)
'(#t #t #t #t #t #t #t #t)
> (map (conjoin odd? integer?) odds)
'(#t #t #t #t #t #t #t #t)
> (map (conjoin odd? integer?) odds)
'(#t #t #t #t #t #t #t #t)
> (map (conjoin odd? integer?) reals)
Error! odd?: contract violation
Error! expected: integer
Error! given: 3.2
; Note: Order of parameters to `conjoin` is important.
Now, how do you convert a list of #t
and #f
values to a single
value? That feels like a job for reduce
.
> (reduce and (list #t #t #f #t #t))
> (reduce and (map real? reals))
> (reduce and (map real? mixed))
a. Do you think this strategy will work? Why or why not?
b. Check your answer experimentally.
As you likely discovered, because and
is a keyword, we cannot use
it with reduce
. Fortunately, Racket provides a procedure, (all
pred? lst)
, that takes a unary predicate and checks each element
of a list with that predicate.
a. Using and
, check each of the cases we looked at in the previous
exercise (e.g., see if reals
, odds
, and mixed
contains only reals).
b. What do you expect as the results of the following?
> (all positive? reals)
> (all positive? odds)
> (define nums (list 1 2.3 4/5 6+7i))
> (all number? nums)
> (all real? nums)
> (all positive? nums)
c. Check your answers experimentally.
Rewrite your average
procedure so that it checks its preconditions,
reporting a separate message for each kind of failed precondition.
In case you’re not sure, we’d expect you to check the following preconditions.
a. Write a procedure, (longer? str1 str2)
that determines whether
str
is longer than
str2`.
b. Using longer?
, sort a list of strings by length.
c. Write a procedure, (longer str1 str2)
that returns whichever
of str1
and str2
is longer. (That is, longer
acts much like max
,
except for strings.)
d. Using longer
, write a procedure, (longest-kernel strings)
, that
finds the longest string in a list of strings. As the name suggests,
longest-kernel
need not check its preconditions.
e. Write a procedure, (longest strings)
, that finds the longest
string in a list of strings. Your procedure should check all of
its preconditions and issue an appropriate error message for each.
Consider a procedure, (list-substitute lst old new)
, that builds a new list by substituting new ... old
whenever old
appears in lst
.
> (list-substitute (list "black" "red" "green" "blue" "black") "black" "white")
("white" "red" "green" "blue" "white")
> (list-substitute (list "black" "red" "green" "blue" "black") "yellow" "white")
("black" "red" "green" "blue" "black")
> (list-substitute null "yellow" "white")
()
a. Document this procedure, making sure to carefully consider the preconditions.
b. Write the husk for this procedure. (That is, just write the precondition tests. You can just return the original list for now.)