# Class 37: Procedures as Values (3)

Back to Procedures as Values (2). On to Procedures as Values (4).

Held: Thursday, 6 November 2003

Summary: We continue our discussion and expermentation with higher-order procedures.

Related Pages:

Assignments

Notes:

• I probably won't have the exams graded until next week.

Overview:

• Procedures that build procedures.
• Why use higher-order procedures?
• Lab.

## Returning Procedures

• It is often useful to have procedures return other procedures.
• For example, the legendary function composition operation that Mathematicians love takes two functions as parameters and returns a new function.

### An Example: Opposite Predicates

• At times, we want a predicate (procedure that returns true or false) that does the opposite of what another predicate does.
• For example, if `odd?` were defined but `even?` were not, we'd simply say that `even?` is the opposite of `odd?`.
• We can write this in longhand as
```(define even?
(lambda (val)
(not (odd? val))))
```
• However, it would be nice to be able to say something closer to the quotation above
```(define even? (negate odd?))
```
• Hence, `negate` is a procedure that builds procedures.
• We can define it as
```(define negate
(lambda (pred?)
(lambda (val)
(not (pred? val)))))
```
• Try it!

### Potential Confusions

• Where's the `lambda` in the definition of `even?`.
• It's hidden in the definition of `negate`.
• Why are there two `lambda` expressions in the definition of `negate`?
• One is the parameter of `negate`. The other is the parameter of the procedure returned by `negate`.

### Other Boolean Higher-Order Procedures

• Similarly, we might want to combine predicates using things similar to the other Boolean operations.
• A problem with `odd?` is that it crashes and burns when given a non-number. Hence, we might like to define an `odd-number?` predicate that holds if its parameter is both odd and a number.
• In Scheme, we'd like to write
```(define odd-number? (both number? odd?))
```
• Hence, we define `both` as
```(define both
(lambda (pred1? pred2?)
(lambda (val)
(and (pred1? val) (pred2? val)))))
```
• Similarly, we can define `either` as
```(define either
(lambda (pred1? pred2?)
(lambda (val)
(or (pred1? val) (pred2? val)))))
```
• Using all of these fun procedures, we might want to try to write a really concise definition of `listp?`
```(define listp?
(either null? (both pair? (compose listp? cdr))))
```
• Unfortunately, this attempt fails.
• Can you tell why?
• Hint; Consider `(define x (+ x 1))`
• Interesting moral: The `lambda` does something special.

### Building Recursive Procedures

• We can use a similar technique to build recursive higher-order procedures.
• For example, consider the problem of counting values in a list for which a particular predicate holds.
• We already know (from yesterday's class) what the result looks like
```(lambda (lst)
(cond
((null? lst) 0)
((pred? (car lst)) (+ 1 (recurse (cdr lst))))
(else (recurse (cdr lst)))))
```
• How do we define a recursive procedure (other than using `define`)? We use `letrec`!
• Hence, we can define a procedure that makes a counter with something like the following
```(define make-counter
(lambda (pred?)
(letrec ((counter
(lambda (lst)
(cond
((null? lst) 0)
((pred? (car lst)) (+ 1 (counter (cdr lst))))
(else (counter (cdr lst)))))))
counter)))
```
• It's now very easy to define procedures that count.
```(define count-odds (make-counter odd?))
(define count-evens (make-counter even?))
```

## Why Use Higher-Order Procedures?

• Most of the topics we've learned until now have led us to be able to do new things or to do things more efficiently.
• You can do more with conditionals than you can without.
• You can do more with recursion than you can without.
• You can access information more efficiently with vectors than with lists.
• Do higher-order procedures give us any new capabilities?
• No
• Do they make our programs more efficient?
• No
• So why learn them?
• They make our code more elegant.
• They make our code more readable.
• If we don't use higher-order procedures to implement our patterns, the primary alternative is to cut-and-paste patterns.
• If something is wrong in the first version, you now have to fix it in many places.
• If you'd used a higher-order procedure, you'd only have to fix it in one place.
• For example, suppose we decide that it's more efficient to include an extra parameter in our counting procedure that keeps track of the number of things we've seen so far.
• Here's an example of writing `count-evens` that way
```(define count-evens
(lambda (lst)
(count-evens-helper lst 0)))
(define count-evens-helper
(lambda (lst count)
(cond
((null? lst) count)
((even? (car lst)) (count-evens-helper (cdr lst) (+ 1 count)))
(else count-evens-helper (cdr lst) count))))
```
• We can redefine `make-counter` to use that strategy.
```(define make-counter
(lambda (pred?)
(letrec ((counter
(lambda (lst count)
(cond
((null? lst) count)
((pred? (car lst)) (counter (cdr lst) (+ count 1)))
(else (counter (cdr lst) count))))))
(lambda (lst) (counter lst 0)))))
```

## Lab

• Are there any other questions on the readings?
• Start the lab.

## History

Thursday, 28 August 2003 [Samuel A. Rebelsky]

• Created automatically.

Back to Procedures as Values (2). On to Procedures as Values (4).

Disclaimer: I usually create these pages on the fly, which means that I rarely proofread them and they may contain bad grammar and incorrect details. It also means that I tend to update them regularly (see the history for more details). Feel free to contact me with any suggestions for changes.

This document was generated by Siteweaver on Tue Dec 9 13:59:53 2003.
The source to the document was last modified on Mon Sep 1 13:30:51 2003.
This document may be found at `http://www.cs.grinnell.edu/~rebelsky/Courses/CS151/2003F/Outlines/outline.37.html`.

You may wish to validate this document's HTML ; ; Check with Bobby

Samuel A. Rebelsky, rebelsky@grinnell.edu