You are either
- porting code from an imperative language (like C) as
literally as possible and they suddenly use
return to
return prematurely from a function.
- using a control struct such as
do, map, for-each
or fold and want to stop "before the end".
The solution is to use an escape continuation.
The form
(let/ec escaper body ...) binds to
escaper
to a procedure of one argument, that when called will return
the argument as the result of the entire
let/ec form.
(define (foo)
(let/ec return
(display 'bar)
(return 42)
(display 'qux)))
> (foo)
bar
42
A more Schemely example is multiplication of numbers.
This function muliplies the numbers in a list. The
function accumulates the result in the variable
product.
(define (mult1 a-list)
(do ([l a-list (cdr l)]
[product 1 (* product (car l))])
[(null? l) product]
(display ". ")))
Now one observes that if zero occurs in the list
the result will also be zero, no matter what
the remaining elements in the list are. The
form
let/ec lets us add this case without
altering the structure of the code.
(define (mult2 a-list)
(let/ec return
(do ([l a-list (cdr l)]
[product 1 (* product (car l))])
[(null? l) product]
(if (= (car l) 0)
(return 0))
(display ". "))))
The difference between the two is illustrated by:
> (mult1 (list 1 2 3 4 5 6 7))
. . . . . . .
5040
> (mult1 (list 1 2 0 4 5 6 7))
. . . . . . .
0
> (mult2 (list 1 2 0 4 5 6 7))
. . 0
> (mult2 (list 1 2 3 4 5 6 7))
. . . . . . .
5040
In plain R5RS you can use this definition:
(define-syntax let/ec
(syntax-rules ()
[(_ name body ...)
(call-with-current-continuation (lambda (name) body ...))]))
If your implementation provide escape continuations or one-shot continuations,
then using these will probably be more efficient.
--
JensAxelSoegaard
Probably some note should be made that C functions with premature
return statements can be rewritten in Scheme using recursion and conditionals rather than explicit escape continuations. I'm not sure what is a good way to do this, however, since any programmer skilled in a language without tail-call optimization has a healthy aversion to recursion when not absolutely necessary. For PLT users for whom the Cookbook is an aid to migrating from another language, perhaps there should be topic like
IdiomRecursiveProgramming?, and this
IdiomPrematureReturn topic should plug recursive programming as something "advanced" Scheme programmers generally do. Although even programmers experienced in recursive programming sometimes find good use for
let/ec. (Perhaps I'm misrepresenting "advanced Scheme programmers" here.)
--
NeilVanDyke - 19 May 2004
I have changed the discussion a little to emphasize that
let/ec can
be used in cases where you want to return prematurely from control struct
even when haven't got access to it's code.
(Which is why I chose a
do loop and not a named let ;-) )
--
JensAxelSoegaard - 22 May 2004