Procedures as values

Course links

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 '()))
      (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 '()))
      (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.

Exercise 4

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

Exercise 5

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.

Exercise 6

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.

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

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.

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.

I am indebted to Professor Ben Gum for his contributions to the development of this lab.