functional programming & tail recursion?

Tim Peters tim_one at email.msn.com
Tue Feb 29 03:39:28 EST 2000


[Dennis E. Hamilton]
> I'm curious,
>
> I can't imagine any language providing a very good result for
>
> 	def fib(n):
>           assert n = int(n) and n = abs(n)
>           if n in [0, 1]:
>               return n
>           return fib(n-1) + fib(n-2)
>
> or any descriptive-language equivalent (Eiffel?).

Neither can I (certainly not Eiffel -- it's as imperative as Python or C++).

> Are you are thinking of descriptive languages that basically produce
> computations by inferences of some kind?  I would want to know the order,
> O(n), of the computation that actually occurs, I guess, in contrast to the
> ease of expression and the appearance of simplicity.

Dennis, I'm lost.  A Scheme programmer wouldn't write the above, if that's
what you think I was saying.  They would usually either write a "memoized"
function (saving a cache of prior results), or a tail-recursive helper
function that passes around a running accumulator, pragmatically equivalent
to the kind of "efficient loop" *you* would write.  See the book I
referenced (SICP) for details.

> I've seen functional-programming languages take the reverse approach:
> applicative operators that construct iterations (in terms of the result
> semantics), on the same principle as map-reduce sorts of operations.  The
> implementation worked well (assuming that closures are inexpensive,
> however).

A day or two ago I posted a msg on "lazy streams", which shows (a Python
spelling of) the  idiomatic Haskell approach to Fibonacci numbers:  define
an infinite stream "fib" consisting of 0, then 1, then "fib" itself
elementwise-added to its own tail.  That's basically a way to spell the
algorithm above.  Here's a complete Haskell defn, avoiding the std library &
"advanced" features so that it's all explicit:

fibs = 0 : 1 : sum fibs (tail fibs)
    where sum (x:xs) (y:ys) = (x+y) : sum xs ys

If you're used to this form of expression, that says very directly that
fib[i] = fib[i-1] + fib[i-2].  But Haskell is most often implemented via a
form of graph mutation, which magically "remembers" as much of the stream as
has ever been generated, and getting the n'th Fibonacci number is actually
linear-time there.

> Coming back to the claim that functional-programming is no good without
> automatic tail-recursion reduction, I am hard pressed to see that as
> *necessary*.  I can get it being recognition of a practice that has worked
> in the absence of explicit iterative or recurrence forms.
>
> Can you shed any light on that?

It's not deep:  consider a loop iterating a trillion times.  If your
language doesn't have loops, and you have to spell it via non-tail-optimized
recursion, you'll never find hardware that can execute it to completion (the
memory burden would be astronomical).  This is pragmatics, not theory:  the
Scheme std's requirement for "properly tail recursive" is a *practical*
necessity.

> PS: I understand that if I have a recurrence, it is easy to make a
> tail-recursive equivalent.  Or a Prolog clause scheme.  I was
> looking at the problem of having a recursive definition and doing the
> work to make it tail-recursive equivalent.

See SICP for details (there are several approaches, some special, some
general, but this is really getting way too specialized for c.l.py -- you
may want to ask on comp.lang.functional, though).

> I don't think one should begrudge an iterative implementation (which is
> pretty immediate) as non-functional!

"functional" implies (among other things) "no side effects".

    i = i+1

is not a functional program.  Saying that iteration is not functional is a
matter of defn, not of moral judgment.

albeit-on-real-hw-nothing-is-functional-under-the-covers-ly y'rs  - tim






More information about the Python-list mailing list