Fundamentals of CS I (CS151 2002F)

**Primary:**
[Skip To Body]
[Front Door]
[Current]
[Glance]
[Honesty]
[Instructions]
[Links]
[Search]
[Syllabus]

**Groupings:**
[Examples]
[Exams]
[Handouts]
[Homework]
[Labs]
[Outlines]
[Readings]
[Reference]

**ECA:**
[About]
[Grades]
[Quizzes]
[Submit Work]
[Change Password]
[Reset Password]

**Miscellaenous:**
[Scheme Reference]
[CS151 2002F Gum]
[CS151 2001S]
[SamR]
[Glimmer Labs]
[schemers.org]

- Introduction
- An Example: Division
- The
`values`

procedure - Kinds of Multiple-Value Expressions
- Using Expressions that Return Multiple Values
- The
`call-with-values`

procedure

When we invoke any of the procedures that we have discussed so far in the course, we get back a single result. However, there are many computations that we can describe most naturally as having two or more results. In this reading, we consider why we might want to return more than one value from a procedure, how to return more than one value, and how to use more than one value.

A
typical example is the division of integers, as taught in elementary
schools: When you divide 647 by 7, you get a quotient of 92 and a remainder
of 3. It is not obvious that one of these results is more important or
more significant than the other, and in fact Scheme provides a primitive
for each one: `quotient`

for the quotient,
`remainder`

for the remainder:

>(quotient 647 7)92 >(remainder 647 7)3

But since the same underlying computation is carried out in either case, it
would make more sense to have a single procedure `divide`

that
would, when invoked, produce both results. One possibility would be to
have `divide`

return a pair whose car is the quotient and
whose cdr is the remainder. However, that means that someone using the
result would have to call `car`

and `cdr`

to extract
them. It would be better to actually return two separate values, as
in the following.

>(divide 647 7)92 3

The procedure call `(divide 647 7)`

is an example of a
multiple-valued expression in Scheme. Evaluating a multiple-valued
expression gives you several results, as the interaction displayed above
shows. Note that this result is not the same as displaying multiple
values nor is it the same as returning a list of values. We are, in
fact, returning many values

from one procedure.

`values`

procedure
All multiple-valued expressions enter Scheme, directly or indirectly,
through calls to the primitive procedure `values`

.
`Values`

is a variable-arity procedure that returns its
arguments without change -- *all* of them.

>(values 650 682 513 861)650 682 513 861

When the `values`

procedure is given only one argument, it returns
that argument without change:

>(values #\a)#\a

It is also possible to call `values`

with no arguments, in which
case, of course, it returns no values:

>(values)

This is not an error or a non-terminating computation, but simply a
computation that produces nothing when it finishes, like a committee that
decides not to issue a report (which is sometimes the most useful thing a
committee can do). You've seen procedures that return nothing before;
`newline`

and `display`

both return nothing.

As a quick example, let's define a procedure
`mixed-number-parts`

that takes a rational number as its
argument and returns its integer part and its proper fractional part:

;;; Procedure: ;;; mixed-number-parts ;;; Parameters: ;;; rat, A rational number ;;; Purpose: ;;; Separate rat into whole and fractional parts. ;;; Produces: ;;; whole, The whole part ;;; fractional, The fractional part ;;; Preconditions: ;;; The parameters must be a rational number. ;;; Postconditions: ;;; rat = whole + fractional (define mixed-number-parts (lambda (num) (let ((integer-part (truncate num))) (values integer-part (- num integer-part)))))

Many kinds of expressions can have multiple values:

- An
`if`

-expression is multiple-valued if the selected branch, the consequent or the alternate, is multiple-valued. - A
`cond`

-expression is multiple-valued if the last subexpression in the selected`cond`

-clause is multiple-valued. - A
`let`

-,`let*`

-,`letrec`

-, or named`let`

-expression is multiple-valued if the last expression in its body is multiple-valued. - A
`begin`

block is multiple-valued if the last expression in the block is multiple-valued. - A
`lambda`

expression is multiple-valued if the last expression in its body is multiple-valued.

However, identifiers, constants,
`and`

-expressions, and `or`

-expressions are never
multiple-valued.

Of course, we have to be careful about where we write multiple-valued
expressions. We can't put one in the test position of an
`if`

-expression, for instance. If the test expression could be
multiple-valued, it might have both `#t`

and `#f`

among its values! No, the test has to produce exactly one value, so that
we know unambiguously whether to evaluate the consequent or the alternate.

Similarly, we can't write a multiple-valued expression as a subexpression of a procedure call, since the procedure to be called has to be some one particular procedure, and each of the arguments that we supply to it has to be some one particular value.

However, we *can* write a multiple-valued expression as the body of
a `lambda`

-expression, thus constructing our own multiple-result
procedures, and this is the context in which you can expect to see them
most frequently.

`call-with-values`

procedureSo how do we use expressions that return more than one value? We can certainly just see the results by typing the multiple-valued expression at the Scheme prompt. However, we sometimes want to use the values of a multiple-valued expression in some further computation instead of returning them directly. The fact that a multiple-valued expression cannot be used as an argument in a procedure call makes it difficult to use the multiple values.

Scheme's solution is another primitive procedure,
`call-with-values`

, that manages the flow of data from a
multiple-valued expression into a larger computation.
`call-with-values`

is a higher-order procedure that takes two
arguments, a *producer* procedure that generates multiple values and a
*consumer* procedure that accepts them.

The producer procedure takes no arguments and has a multiple-valued
expression as its body. `call-with-values`

invokes the producer
and collects all of the values that it returns.
`call-with-values`

then invokes the consumer, providing the
collected values as arguments. The arity of the consumer must therefore
be compatible with the number of values delivered by the producer.
`call-with-values`

returns whatever the consumer returns.

Suppose, for instance, that we want to recover the integer part and the
proper fractional part of 1173/83 and then subtract the fractional part
from the integer part. Here's how we could invoke
`call-with-values`

to perform the computation:

>(call-with-values (lambda () (mixed-number-parts 1173/83)) -)1151/83

The producer in this case is
`(lambda () (mixed-number-parts 1173/83)`

,
which returns two values when invoked. The consumer is
`-`

, which accepts two values and returns their difference.

As a more practical example, let's define a procedure that takes two
arguments, a predicate `pred`

and a list `lst`

, and
returns two lists, one consisting of all of the elements of `lst`

that satisfy `pred`

, the other consisting of all of the elements
of `lst`

that do *not* satisfy `pred`

. Here's
the procedure's six P's:

;;; Procedure: ;;; partition ;;; Parameters: ;;; pred?, a predicate ;;; lst, a list ;;; Purpose: ;;; Separate the list into two parts, those that meet the ;;; predicate and those that fail to meet the predicate. ;;; Produces: ;;; ins, a list ;;; outs, a list ;;; Preconditions: ;;; pred? can be applied to every element of lst. ;;; Postconditions: ;;; Every element of ins is in lst. ;;; Every element of outs is in lst. ;;; Every element of lst appears in exactly one of ins and outs. ;;; pred? holds for every element of ins. ;;; pred? fails to hold for every element of outs.

Our basic strategy is list recursion. In the base case, where
`lst`

is the empty list, we want to return two lists, both empty.
That's easy -- we'll just write `(values null null)`

. In any
other case, we divide `lst`

into its car and its cdr and invoke
the procedure recursively to deal with the cdr. The recursive call will
return two lists: the list of elements of the cdr that satisfy
`pred`

, and the list of elements of the cdr that do not. We
cons the car of the list onto one or the other of these recursive results,
depending on whether it does or does not satisfy `pred`

, and
return both the result of the cons and the other recursive result.

Here's a start, with some key parts missing

(define partition (lambda (pred? lst) ; If there are no values in the list, (if (null? lst) ; Neither ins nor outs contain anything. (values null null) ; Otherwise, do a recursive call using the rest of the list. ... ; Let ins and outs be the two parts of the recursive result. ... ; If the predicate holds for the first element, (if (pred? (car lst)) ; Add the first element to ins. (values (cons (car lst) ins) outs) ; Otherwise, add the first element to outs. (values ins (cons (car lst) outs))))))))

Now we need to insert the code to handle the recursive call and the
naming of the results from the recursive call. Since the recursive
call returns more than one value, we probably need to bundle it up
and use `call-with-values`

.

; Otherwise, do a recursive call using the rest of the list. (call-with-values (lambda () (partition pred? (cdr lst)))

Now we need to write the consumer that will use the two results of
that recursive call. Hence, it will be a procedure of two parameters,
`ins`

and `outs`

.

; Let ins and outs be the two parts of the recursive result. (lambda (ins outs)

Putting it all together, we get the following.

(define partition (lambda (pred? lst) ; If there are no values in the list, (if (null? lst) ; Neither ins nor outs contain anything. (values null null) ; Otherwise, do a recursive call using the rest of the list. (call-with-values (lambda () (partition pred? (cdr lst))) ; Let ins and outs be the two parts of the recursive result. (lambda (ins outs) ; If the predicate holds for the first element, (if (pred? (car lst)) ; Add the first element to ins. (values (cons (car lst) ins) outs) ; Otherwise, add the first element to outs. (values ins (cons (car lst) outs))))))))

Notice how this example uses `call-with-values`

to manage the
transfer of two values from the multiple-valued expression ```
(partition
pred? (cdr lst))
```

into the part of the computation that consumes those
values.

Think of the body of the producer, the expression

, as ready to supply its results when asked.
Think of the body of the consumer, the inner ```
(partition
pred? (cdr lst))
```

`if`

-expression,
as ready to receive those results and operate on them to construct
the final values that the recursive call will return. The role of
`call-with-values`

is to activate and mediate these two
packaged components of the overall computation.

Some unknown date in history [John Stone and/or Henry Walker]

- Created.

Some unknown date in Fall 2000 [Samuel A. Rebelsky]

- Extracted from a page by John stone that was previously at
`http://www.math.grin.edu/~stone/courses/scheme/multiple-valued-procedures.xhtml`

and dated April 3 2000. That page is now (November 2002) at`http://www.cs.grinnell.edu/~stone/courses/scheme/spring-2000/multiple-valued-procedures.xhtml`

. - Reformatted.
- Some updates.
- This version available at
`http://www.cs.grinnell.edu/~rebelsky/Courses/CS151/2000F/Readings/multivalue-proc.html`

.

Thursday, 12 April 2001 [Samuel A. Rebelsky]

- Reformatted.
- Added history.

Friday, 13 April 2001 [Samuel A. Rebelsky]

- Added six Ps for the
`partition`

procedure. - This version available at
`http://www.cs.grinnell.edu/~rebelsky/Courses/CS151/2001S/Readings/multivalue-proc.html`

.

Tuesday, 12 November 2002 [Samuel A. Rebelsky]

- Added section headings and table of contents.
- Rearranged sections.
- Some other minor modifications to the writing.
- Rewrote partition without an inner kernel.
- Added comments within the
`partition`

procedure as well as the partial code for that procedure. - This version available at
`http://www.cs.grinnell.edu/~rebelsky/Courses/CS151/2002F/Readings/multivalue-proc.html`

.

**Primary:**
[Skip To Body]
[Front Door]
[Current]
[Glance]
[Honesty]
[Instructions]
[Links]
[Search]
[Syllabus]

**Groupings:**
[Examples]
[Exams]
[Handouts]
[Homework]
[Labs]
[Outlines]
[Readings]
[Reference]

**ECA:**
[About]
[Grades]
[Quizzes]
[Submit Work]
[Change Password]
[Reset Password]

**Miscellaenous:**
[Scheme Reference]
[CS151 2002F Gum]
[CS151 2001S]
[SamR]
[Glimmer Labs]
[schemers.org]

**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 Mon Dec 2 08:41:56 2002.

The source to the document was last modified on Tue Nov 12 21:44:42 2002.

This document may be found at `http://www.cs.grinnell.edu/~rebelsky/Courses/CS151/2002F/Readings/multivalue-proc.html`

.

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

Samuel A. Rebelsky, rebelsky@grinnell.edu