Procedures as values

Operator sections

An operator section is a procedure that is derived from another procedure by ``filling in'' some but not all of its arguments. For instance, the double procedure defined by

(define double
  (lambda (n)
    (* 2 n)))

qualifies as an operator section, since it fills in the first argument to the * procedure with the particular value 2. Operator sections are often used as arguments to higher-order procedures such as list-of, tallier, and generate-list from the first lab on procedures as values.

For instance, we could construct a procedure that counts the number of occurrences of the symbol 'n/a (``not available'') in a given list as (tallier (lambda (whatever) (eq? 'n/a whatever))). Here the value of the lambda-expression is an operator section of eq?, with the first argument filled in with the particular value 'n/a.

We can even define higher-order procedures to construct operator sections for us. Such procedures are not primitives, but they are easily defined -- I'll use the name left-section for a higher-order procedure that takes a procedure of two arguments and a value to drop in as its first argument, and returns the relevant operator section:

(define left-section
  (lambda (procedure filler-in)
    (lambda (expected)
      (procedure filler-in expected))))

So we could define double as (left-section * 2) and (lambda (whatever) (eq? 'n/a whatever)) as (left-section eq? 'n/a).


Exercise 1

Define and test the analogous 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.)


Exercise 2

Using the generate-list procedure from the first lab on procedures as values 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:

> (powers-of-two 7)
(1 2 4 8 16 32 64)

Exercise 3

Define a procedure bounded-mu that takes two arguments, a predicate pred and a natural number limit, and returns the least natural number less than limit that satisfies pred, 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.


Filtering

To filter a list is to examine each of its elements in turn, retaining some for a new list while eliminating others. For instance, given a list of integers, the following procedure filters it to remove the negative ones:

(define remove-negatives
  (lambda (ls)
    (cond ((null? ls) null)
          ((negative? (car ls)) (remove-negatives (cdr ls)))
          (else (cons (car ls) (remove-negatives (cdr ls)))))))

We could write similar procedures to remove the whitespace characters from a list of characters, or to exclude any occurrences of the symbol 'n/a from a list:

(define remove-whitespace
  (lambda (ls)
    (cond ((null? ls) null)
          ((char-whitespace? (car ls)) (remove-whitespace (cdr ls)))
          (else (cons (car ls) (remove-whitespace (cdr ls)))))))

(define remove-n/a-symbols
  (lambda (ls)
    (cond ((null? ls) null)
          ((eq? 'n/a (car ls)) (remove-n/a-symbols (cdr ls)))
          (else (cons (car ls) (remove-n/a-symbols (cdr ls)))))))

Similar filtering procedures occur so frequently that it's useful to have a higher-order procedure to construct them. Using the method described in the first lab on procedures as values, we can easily define such a procedure:

(define remove
  (lambda (predicate)
    (letrec ((recurrer (lambda (ls)
                         (cond ((null? ls) null)
                               ((predicate (car ls)) (recurrer (cdr ls)))
                               (else (cons (car ls) (recurrer (cdr ls))))))))
      recurrer)))

(define remove-negatives (remove negative?))
(define remove-whitespace (remove char-whitespace?))
(define remove-n/a-symbols (remove (left-section eq? 'n/a)))

Exercise 4

Here is an interesting list of natural numbers:

(define duplicated-membership-numbers (list 1471 4270))

Define a Scheme procedure remove-multiple-voters that takes a list of non-empty lists as its argument and filters out of it the lists in which the first element is also an element of duplicated-membership-numbers.


Exercise 5

Define the intersection procedure (from the lab on local binding and recursion) using remove and right-section.


Exercise 6

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/procedures-as-values-continued.xhtml

created October 30, 1997
last revised March 17, 2000

John David Stone (stone@cs.grinnell.edu)