Procedures as values

Exercise 1

Here are three procedures with similar structures:

;;; 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.

(define whitespace-tally
  (lambda (ls)
    (cond ((null? ls) 0)
          ((char-whitespace? (car ls))
           (+ (whitespace-tally (cdr ls)) 1))
          (else (whitespace-tally (cdr ls))))))

;;; 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.

(define even-tally
  (lambda (ls)
    (cond ((null? ls) 0)
          ((even? (car ls)) (+ (even-tally (cdr ls)) 1))
          (else (even-tally (cdr ls))))))

;;; 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.

(define symbol-tally
  (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.

Exercise 2

How would you use the tallier procedure that you defined in exercise 1 to create procedures to determine

Exercise 3

Here are three procedures, each of which takes a natural number as argument and returns a list:

;;; 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.

(define generate-list-of-squares
  (lambda (len)
    (let kernel ((remaining len)
                 (so-far null))
      (if (zero? remaining)
          so-far
          (let ((next (- remaining 1)))
            (kernel next (cons (* next next) so-far)))))))

;;; 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.

(define generate-list-of-hyphen-strings
  (lambda (len)
    (let kernel ((remaining len)
                 (so-far null))
      (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 null))
      (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.

Develop 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.

Exercise 4

Use map to give a concise definition of a procedure association-list-keys that takes one argument, an association list, and returns a list of the keys in that association list.

Exercise 5

Use map to write a version of square-each-element described in the recursion with lists lab, which returns a list of the squares of the numbers in the original list.

Exercise 6

Use apply to give an extremely concise definition of the procedure sum, which returns the sum of a list of numbers.

Exercise 7

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 as a model.

Exercise 8

Using the generate-list procedure from exercise 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)

Exercise 9

Define 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.

Exercise 10

Define the intersection procedure (from exercise 6 in the lab on local binding and recursion) using the remove procedure presented in today's reading and the right-section procedure developed in exercise 7 above.

Exercise 11

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.


This document is available on the World Wide Web as

http://www.cs.grinnell.edu/~stone/courses/scheme/labs/procedures-as-values.xhtml

created March 24, 1997
last revised October 8, 2002

John David Stone (stone@cs.grinnell.edu) and Ben Gum (gum@cs.grinnell.edu)