CSC 153: Computer Science Fundamentals Grinnell College Spring, 2005 Laboratory Exercise Reading

# Procedures as First-Class Citizens

## Abstract

Procedures are data in Scheme: They can be passed as arguments to other procedures, returned as results by other procedures, and bound to variables. It is common, in the course of a Scheme program, to invoke a procedure that is constructed -- computed -- by the program itself. This gives Scheme a kind of expressive power that few other programming languages share.

## Procedure Parameters

Let's consider, first, a simple case in which a procedure is passed as an argument to another procedure. The `every?` procedure defined below takes two arguments, a predicate `pred` and a list `ls`, and determines whether or not the predicate is true of every element of the list.

```(define every?
(lambda (pred ls)
;Pre-condition:  pred is a procedure; ls is a list
;Post-conditions:  returns error if pre-conditions are not met
;                  returns #t if pred is true for every element of list
; precondition tests
(if (not (procedure? pred))
(error 'every? "The first argument must be a predicate"))
(if (not (list? ls))
(error 'every? "The second argument must be a list"))
(every-kernel pred ls)))

(define every-kernel
(lambda (pred ls)
;Pre-condition:  pred is a procedure; ls is a list
;Post-conditions:  returns #t if pred is true for every element of list
(or (null? ls)
(and (pred (car ls))         ; Invoke the predicate here!
(every-kernel pred (cdr ls))))))
```

As in many previous examples in the course, the code for `every?` actually just performs error checking that is needed just at the start. Then `every?` does the actual work of going through the list.

The `every-kernel` procedure returns `#t` immediately if it is given an empty list. Otherwise, it takes the list apart into its car and its cdr and applies the given predicate to the car. If the invocation of the predicate returns `#f`, this value is immediately returned by `every-kernel`; but if the predicate is true of the car, `every-kernel` is invoked recursively to continue the investigation, searching through the cdr to make sure that the predicate is true of all of its elements as well.

Even though it is passed in as an argument, one can issue a procedure call to the predicate, invoking it under its local name, `pred`. Whatever predicate is passed in will be invoked at that point. The predicate that is invoked will be different in different calls to `every?`; that's okay with Scheme.

Here are some applications of `every?`:

```> (every? odd? '(1 3 5 7 9))   ; True: 1, 3, 5, 7, and 9 are odd.
#t

> (every? odd? '(1 3 5 7 10))  ; False: 10 is not odd.
#f

> (every? odd? '(1 3 5 8 foo)) ; False: 8 is not odd.
#f

> (every? (lambda (n)
(and (integer? n) (zero? (remainder n 7))))
'(0 -35 91 7 -1001))
; True: every element of the list is an integer that is
;   divisible by 7.
#t

> (every? procedure? (list + number? cons procedure? every? list))
; True: +, number?, cons, procedure?, every?, and list are all procedures.
#t
```

As these examples show, the predicate that is given as the argument to `every?` can be either built-in or programmer-defined.

## Some Built-in Procedures with Procedure Arguments

Scheme provides several built-in procedures that take procedures as arguments.

### map

One such procedure is `map`, which takes as arguments a procedure and a list and applies the procedure to each element of the list, collecting the results into a list:

```
(define square
(lambda (n)
;Pre-condition:  n is a number
;Post-condition:  n-squared is returned
(* n n)))

> (map square '(3 -9 4.1 8/3 0))
(9 81 16.81 64/9 0)
```

The `map` procedure works as if it were defined like this:

```(define map
(lambda (operation ls)
;Pre-condition:  operation is a procedure; ls is a list
;Post-condition: the operation is applied to every element of ls
(if (null? ls)
'()
(cons (operation (car ls)) (map operation (cdr ls))))))
```

Here's how `map` could be used to write the `double-each-element` procedure from the first laboratory exercise on recursion.

```(define double-each-element
(lambda (ls)
;Pre-condition:  ls is a list of numbers
;Post-condition:  returns a list with every number on ls doubled
(map (lambda (x) (* x 2)) ls)))
```
Using `map` several times provides a simple mechanism to capitalize all letters in a string:
```(define capitalize
(lambda (str)
;Pre-condition:  str is a character string
;Post-condition:  str is returned with all letters capitalized
(list->string (map char-upcase (string->list str)))
)
)
```

### apply

Another useful higher-order procedure that is built into Scheme is `apply`, which takes a procedure and a list as arguments and invokes the procedure, giving it the elements of the list as its arguments:

```> (apply string=? (list "foo" "foo"))
#t

> (apply * '(3 4 5 6))
360

> (apply append '((a b c) (d) (e f) () (g h i)))
(a b c d e f g h i)
```

### eval

The higher-order procedure `eval` is related to `apply`, in that `eval` evaluates the expression that follows it:

```> (eval '(< 1 2 4 6 9))   ;  the numbers 1 2 4 6 9 are in ascending
#t

> (eval '(< 1 2 7 6 9))   ;  < applied to these numbers is false
#f

> (eval (cons '< '(1 2 4 6 9)))  ; form the list and evaluate it as above
#t
```

The very first lab mentioned that the basic Scheme environment follows a read-eval-print cycle. Here, you see that `eval` is a Scheme procedure that does this evaluation.

This document is available on the World Wide Web as

```http://www.cs.grinnell.edu/~walker/courses/153.sp05/readings/reading-procedure-arguments.shtml
```

 created March 24, 1997 by John David Stone last revised January 31, 2005 by Henry M. Walker  For more information, please contact Henry M. Walker at walker@cs.grinnell.edu.