| CSC 153 | Grinnell College | Spring, 2009 |
| Computer Science Fundamentals | ||
This laboratory exercise provides students with experience exploring patterns of problem solving in the development of higher-order procedures.
Here are three procedures with similar structures:
(define whitespace-tally
;;; whitespace-tally: determine how many elements of a given
;;; list of characters are whitespace characters
;;; Given:
;;; LS, a list of characters.
;;; Result:
;;; TALLY, an integer.
;;; Preconditions:
;;; None.
;;; Postcondition:
;;; TALLY is the number of elements of LS that are
;;; whitespace characters.
(lambda (ls)
(cond ((null? ls) 0)
((char-whitespace? (car ls))
(+ (whitespace-tally (cdr ls)) 1))
(else (whitespace-tally (cdr ls))))))
(define even-tally
;;; even-tally: determine how many elements of a given list
;;; of exact integers are even
;;; Given:
;;; LS, a list of exact integers.
;;; Result:
;;; TALLY, an integer.
;;; Preconditions:
;;; None.
;;; Postcondition:
;;; TALLY is the number of even elements of LS.
(lambda (ls)
(cond ((null? ls) 0)
((even? (car ls)) (+ (even-tally (cdr ls)) 1))
(else (even-tally (cdr ls))))))
(define symbol-tally
;;; symbol-tally: determine how many elements of a given
;;; list are symbols
;;; Given:
;;; LS, a list.
;;; Result:
;;; TALLY, an integer.
;;; Preconditions:
;;; None.
;;; Postcondition:
;;; TALLY is the number of elements of LS that are symbols.
(lambda (ls)
(cond ((null? ls) 0)
((symbol? (car ls)) (+ (symbol-tally (cdr ls)) 1))
(else (symbol-tally (cdr ls))))))
By abstracting what these procedures have in common, develop a procedure
tallier that takes a predicate as its argument and constructs
and returns a specialized tallying procedure that counts the number of
elements of a given list that satisfy the predicate.
How would you use the tallier procedure that you defined in
step 1 to create procedures to determine
'n/a (``not
available'') in a list? (Hint: Use a lambda-expression.)Here are three procedures, each of which takes a natural number as argument and returns a list:
(define generate-list-of-squares
;;; generate-list-of-squares: construct and return a list of
;;; the squares of natural numbers less than a given natural
;;; number
;;; Given:
;;; LEN, an integer.
;;; Result:
;;; LS, a list.
;;; Precondition:
;;; LEN is exact and not negative.
;;; Postconditions:
;;; (1) The length of LS is LEN.
;;; (2) For every natural number k less than LEN, the element
;;; at position k of LS is the square of k.
(lambda (len)
(let kernel ((remaining len)
(so-far '()))
(if (zero? remaining)
so-far
(let ((next (- remaining 1)))
(kernel next (cons (* next next) so-far)))))))
(define generate-list-of-hyphen-strings
;;; generate-list-of-hyphen-strings: construct and return
;;; a list of strings of hyphens of length less than
;;; a given natural number.
;;; Given:
;;; LEN, an integer.
;;; Result:
;;; LS, a list.
;;; Precondition:
;;; LEN is exact and not negative.
;;; Postconditions:
;;; (1) The length of LS is LEN.
;;; (2) For every natural number k less than LEN, the element
;;; at position k of LS is the a string of hyphens of length
;;; k.
(lambda (len)
(let kernel ((remaining len)
(so-far '()))
(if (zero? remaining)
so-far
(let ((next (- remaining 1)))
(kernel next (cons (make-string next #\-) so-far)))))))
;;; generate-list-of-termials: construct and return a list of
;;; the ``termials'' of natural numbers less than a given
;;; natural number
;;; Given:
;;; LEN, an integer.
;;; Result:
;;; LS, a list.
;;; Precondition:
;;; LEN is exact and not negative.
;;; Postconditions:
;;; (1) The length of LS is LEN.
;;; (2) For every natural number k less than LEN, the element
;;; at position k of LS is the sum of the natural numbers
;;; less than or equal to k.
(define generate-list-of-termials
(lambda (len)
(let kernel ((remaining len)
(so-far '()))
(if (zero? remaining)
so-far
(let ((next (- remaining 1)))
(kernel next (cons (termial next) so-far)))))))
(The termial
procedure was defined in the reading on recursion with integers. It
computes the sum of all the natural numbers up to and including its
argument.)
Apply each of these procedures to a few small natural numbers to see what they do.
Design, write, and test a generate-list procedure that
abstracts the common structure of these three definitions.
Generate-list should take, as its argument, any one-argument
procedure procedure that can be applied to a natural number.
Generate-list should return, as its value, a procedure that,
when applied to a natural number len, constructs and returns a
list of the results of applying procedure to every natural
number less than len.
The map procedure can actually take more than two arguments,
if all of the extras are lists, and the arity of the procedure that
map applies is increased to match:
> (map string-append '("left" "start" "beginning")
'("-to-" "-to-" "-to-")
'("right" "finish" "end"))
("left-to-right" "start-to-finish" "beginning-to-end")
> (map cons '(a b c) '(d e f))
((a . d) (b . e) (c . f))
Using map, define a procedure pairwise-sum that
takes as arguments two lists of numbers, equal in length, and returns a new
list whose components are the sums of the corresponding components of the
arguments.
Design, write, and test a procedure dot-product that takes as
arguments two lists of numbers, equal in length, and returns the sum of the
products of corresponding elements of the arguments. Here are two sample
calls:
> (dot-product '(1 2 4 8) '(11 5 7 3)) 73 ; ... because (1 x 11) + (2 x 5) + (4 x 7) + (8 x 3) = ; 11 + 10 + 28 + 24 = 73 > (dot-product '() '()) 0 ; ... because in this case there are no products to add
One can use apply and map to give an extremely concise
definition of this procedure.
Develop the higher-order procedure right-section, which takes
a procedure of two arguments and a value to drop in as its second
argument, and returns the operator section that expects the first
argument. (For instance, (right-section expt 3) is a
procedure that computes the cube of any number it is given.) You can use
the definition of left-section from today's reading on
procedures as values as a model.
Using the generate-list procedure from step 3, above, and
an operator section, define a procedure powers-of-two that
constructs and returns a list of powers of two, in ascending order, given
the length of the desired list. Here's a sample call:
> (powers-of-two 7) (1 2 4 8 16 32 64)
Design, write, and test a procedure bounded-mu that takes two
arguments, a predicate predicate and a natural number
limit, and returns the least natural number less than
limit that satisfies predicate, or
#f if there is no such number.
Use bounded-mu to find the least natural number less than 1000
that leaves a remainder of 5 when divided by 7, a remainder of 7 when
divided by 11, and a remainder of 11 when divided by 13.
The filters constructed by remove are designed to
exclude list elements that satisfy a given predicate. Define a
higher-order procedure filter that returns a filtering
procedure that retains the elements that satisfy a given predicate
(excluding those that fail to satisfy it). For instance, applying
the filter (filter even?) to a list of integers should return
a list consisting of just the even elements of the given list.
Much of this lab comes from materials by John David Stone. Dr. Stone, in turn, acknowledges Professor Ben Gum for his contributions to the development of this lab.
This document is available on the World Wide Web as
http://www.cs.grinnell.edu/~walker/courses/153.sp09/labs/lab-procedures-as-values.shtml
|
created 24 March 1997 by John David Stone revised 2 March 2005 by John David Stone last revised 2 April 2008 by Henry M. Walker |
|
| For more information, please contact Henry M. Walker at walker@cs.grinnell.edu. |