Not a very good example. The n-th term of the Fibonacci sequence can be computed straightforwardly with an imperative loop:
def mul(a,b,c,d):
x = a*c + a*d + b*c
y = a*c + b*d
return x,y
def fib(n):
a,b,c,d = 0,1,1,0
while n:
while n % 2 == 0:
c,d = mul(c,d,c,d)
n //= 2
a,b = mul(a,b,c,d)
n -= 1
return a
IMO a better example is a loop where a producer and a consumer interact in the following way:
(0) Get an element from the producer. If the producer is depleted, exit the loop.
(1) Put the element into the consumer. If the consumer is saturated, exit the loop.
With mutual recursion, you can do something like:
(* Standard ML syntax. This can be easily adapted to other languages.
*
* Naming convention:
*
* is: input stream
* os: output stream
* il: input leftovers
* ol: output leftovers
*
* Input/output are as in C++'s input/output iterators. In fact, this
* code is essentially C++'s std::transform, except not retarded.
*)
structure I : INPUT_STREAM = ...
structure O : OUTPUT_STREAM = ...
datatype result = Depleted of I.leftovers * O.stream
| Saturated of I.stream * O.leftovers
fun first (is, os) =
case I.fetch is of
I.Cont (x, is) => second (x, is, os)
| I.Done il => Depleted (il, os)
and second (x, is, os) =
case O.store (x, os) of
O.Cont os => first (is, os)
| O.Done ol => Saturated (is, ol)
Note how either `first` or `second` can break the loop by simply not calling the other. With an imperative loop, you have three choices, none of them pleasant:
(0) Assume that input streams cannot be depleted.
(1) Assume that output streams cannot be saturated. This is what C++'s `std::transform` does.
(2) Use an ugly “break” in the middle of the loop.
(0) Get an element from the producer. If the producer is depleted, exit the loop.
(1) Put the element into the consumer. If the consumer is saturated, exit the loop.
With mutual recursion, you can do something like:
Note how either `first` or `second` can break the loop by simply not calling the other. With an imperative loop, you have three choices, none of them pleasant:(0) Assume that input streams cannot be depleted.
(1) Assume that output streams cannot be saturated. This is what C++'s `std::transform` does.
(2) Use an ugly “break” in the middle of the loop.