231

A reddit thread brought up an apparently interesting question:

Tail recursive functions can trivially be converted into iterative functions. Other ones, can be transformed by using an explicit stack. Can every recursion be transformed into iteration?

The (counter?)example in the post is the pair:

(define (num-ways x y)
  (case ((= x 0) 1)
        ((= y 0) 1)
        (num-ways2 x y) ))

(define (num-ways2 x y)
  (+ (num-ways (- x 1) y)
     (num-ways x (- y 1))
nbro
  • 15,395
  • 32
  • 113
  • 196
Tordek
  • 10,628
  • 3
  • 36
  • 67
  • 4
    I don't see how this is a counter-example. The stack technique will work. It won't be pretty, and I'm not going to write it, but it is doable. It appears akdas acknowledges that in your link. – Matthew Flaschen May 31 '09 at 10:00
  • Your (num-ways x y) is just (x+y) `choose` x = (x+y)!/(x!y!), which doesn't need recursion. – ShreevatsaR Jul 07 '09 at 21:18
  • 3
    Duplicate of: http://stackoverflow.com/questions/531668/ – H H Nov 02 '09 at 17:03
  • I would say that recursion is merely a convenience. – Déjà vu Mar 27 '18 at 07:40
  • 3
    Possible duplicate of [Which recursive functions cannot be rewritten using loops?](https://stackoverflow.com/questions/531668/which-recursive-functions-cannot-be-rewritten-using-loops) – arghtype Feb 07 '19 at 21:42
  • The other thing I might ask is.. wouldn't the compiler be able to turn recursion into iteration? – Khaled.K Jun 11 '19 at 03:46

18 Answers18

216

Can you always turn a recursive function into an iterative one? Yes, absolutely, and the Church-Turing thesis proves it if memory serves. In lay terms, it states that what is computable by recursive functions is computable by an iterative model (such as the Turing machine) and vice versa. The thesis does not tell you precisely how to do the conversion, but it does say that it's definitely possible.

In many cases, converting a recursive function is easy. Knuth offers several techniques in "The Art of Computer Programming". And often, a thing computed recursively can be computed by a completely different approach in less time and space. The classic example of this is Fibonacci numbers or sequences thereof. You've surely met this problem in your degree plan.

On the flip side of this coin, we can certainly imagine a programming system so advanced as to treat a recursive definition of a formula as an invitation to memoize prior results, thus offering the speed benefit without the hassle of telling the computer exactly which steps to follow in the computation of a formula with a recursive definition. Dijkstra almost certainly did imagine such a system. He spent a long time trying to separate the implementation from the semantics of a programming language. Then again, his non-deterministic and multiprocessing programming languages are in a league above the practicing professional programmer.

In the final analysis, many functions are just plain easier to understand, read, and write in recursive form. Unless there's a compelling reason, you probably shouldn't (manually) convert these functions to an explicitly iterative algorithm. Your computer will handle that job correctly.

I can see one compelling reason. Suppose you've a prototype system in a super-high level language like [donning asbestos underwear] Scheme, Lisp, Haskell, OCaml, Perl, or Pascal. Suppose conditions are such that you need an implementation in C or Java. (Perhaps it's politics.) Then you could certainly have some functions written recursively but which, translated literally, would explode your runtime system. For example, infinite tail recursion is possible in Scheme, but the same idiom causes a problem for existing C environments. Another example is the use of lexically nested functions and static scope, which Pascal supports but C doesn't.

In these circumstances, you might try to overcome political resistance to the original language. You might find yourself reimplementing Lisp badly, as in Greenspun's (tongue-in-cheek) tenth law. Or you might just find a completely different approach to solution. But in any event, there is surely a way.

Ian
  • 4,421
  • 1
  • 20
  • 17
  • 14
    Isn't Church-Turing yet to be proven? – Liran Orevi Jul 08 '09 at 07:57
  • 4
    Here's a really short outline: pick two models of computation A and B. Prove that A is at least as powerful as B by writing an interpreter of B using A. Do this in both directions, and you have shown that A and B have equivalent power. Consider that machine code is almost the Turing-machine model, and that lisp interpreters/compilers exist. The debate should be over. But for more information, see: http://www.alanturing.net/turing_archive/pages/Reference%20Articles/The%20Turing-Church%20Thesis.html – Ian Jul 31 '09 at 05:41
  • Ian, I'm not sure that proves each has equivalent power, only that each uses equivalent power. Use demonstrates certain capabilities, but not necessarily the extent of them. – eyelidlessness Nov 02 '09 at 17:20
  • 16
    @eyelidlessness: If you can implement A in B, it means B has at least as much power as A. If you cannot execute some statement of A in the A-implementation-of-B, then it's not an implementation. If A can be implemented in B and B can be implemented in A, power(A) >= power(B), and power(B) >= power(A). The only solution is power(A) == power(B). – Tordek Jan 01 '10 at 07:31
  • Would this answer depend on how restrictive the (iterative) language is? What if the language has very limited use of GOTO statements? How is it possible for the program to "remember" the instruction at which the recursion/iteration was last called from without some kind of return address? – T. Webster Dec 13 '11 at 21:03
  • @T.Webster You can't get much more limited than a [Turing Machine](http://en.wikipedia.org/wiki/Turing_machine) - gotos? ha waaay too advanced feature. You can look at the informal definition to see what you need to get the same power as any modern iterative programming language. – Voo Feb 03 '12 at 01:51
  • 8
    re: 1st paragraph: You are speaking about equivalence of models of computation, not the Church-Turing thesis. The equivalence was AFAIR proved by Church and/or Turing, but it is not the thesis. The thesis is an experimental fact that everything intuitively computable is computable in strict mathematical sense (by Turing machines/recursive functions etc.). It could be disproven if using laws of physics we could build some nonclassical computers computing something Turing machines cannot do (e.g. halting problem). Whereas the equivalence is a mathematical theorem, and it will not be disproven. – sdcvvc Apr 14 '12 at 23:20
  • 14
    How the heck did this answer get any positive votes? First it mixes up Turing completeness with the Church-Turing thesis, then it makes a bunch of incorrect handwaving, mentioning "advanced" systems and dropping lazy infinite tail recursion (which you can do in C or any Turing complete language because.. uh.. does anyone know what Turing complete means?). Then a hopeful hand-holding conclusion, like this was a question on Oprah and all you need is to be positive and uplifting? Horrid answer! – ex0du5 May 24 '12 at 05:00
  • 12
    And the bs about semantics??? Really? This is a question on syntactic transformations, and somehow it's became a great way to name drop Dijkstra and imply you know something about the pi-calculus? Let me make this clear: whether one looks at the denotational semantics of a language or some other model will have no bearing on the answer to this question. Whether the language is assembly or a generative domain modeling language means nothing. Its only about Turing completeness and transforming "stack variables" to "a stack of variables". – ex0du5 May 24 '12 at 05:13
  • 1
    If any recursive function can be transformed into a non-recursive version, then why don't compilers do this? It is common advice to write non-recursive methods if possible because of performance overhead and SO issues with a recursive method. – morpheus Apr 14 '13 at 04:56
  • @morpheus, in fact compilers for functional languages (and many lisps) routinely apply several techniques to automatically convert a large category of recursive-looking definitions into object code that doesn't overuse stack space. But in any event, object code can be considered as iterative by considering the CPU to run a fetch-execute iteration. – Ian Jul 23 '14 at 05:27
  • Some other practical reasons for converting recursive functions to other implementations: (1) You want to write an iterator class over a complex data structure, but you don't have the equivalent of C#'s yield. (2) You have many executing co-processes, and you do not want to (or cannot) implement them as separate threads. This is seen in algorithms for executing data-flow graphs, where assigning a thread to each vertex may be impractical. – Wheezil Dec 01 '14 at 14:35
  • 1
    @morpheus Iteration isn't magic. If a recursive function calls itself more than once during a single call (e.g. [merge sort](http://stackoverflow.com/a/2531131/3342206)), then it requires unbounded memory space, and implementing it via iteration won't change that, you'll just be manually managing a stack. But, many naturally recursive algorithms only call themselves once per call and can be written as a tail call. Tail calls can work in fixed memory space, but require special treatment from the compiler. Telling users to manually convert to iteration means compiler writers can do other stuff. – 8bittree Sep 13 '16 at 16:52
  • How about the inverse? Can you always turn a iterative function into an recursive one? – user1870400 May 02 '17 at 17:39
  • @user1870400 ,yes, you can. By one definition, iterative functions are primitive-recursive to begin with. But if your goal is to write it without explicit looping constructs, then a correspondingly-designed recursive call stands in for the construct. The details are part of any course on functional programming. – Ian May 10 '17 at 14:26
  • 1
    [donning asbestos underwear] deserves 50 points in itself. :-) – user721025 Feb 06 '19 at 03:20
  • @user1870400 an iterative function is already a (primitive-)recursive function. The operation is vacuous. – Ian Feb 07 '19 at 05:02
  • 1
    @Ian *Knuth offers several techniques in "The Art of Computer Programming"*. Could You please tell which volume of the book you are talking about? Also, could you please tell any other source which tells a general techniques to avoid recursion? – ImBatman Oct 03 '20 at 17:10
  • @Ian I'd also like to know which TAOCP volume you were referring to. – Milos Nov 20 '21 at 06:35
55

Is it always possible to write a non-recursive form for every recursive function?

Yes. A simple formal proof is to show that both µ recursion and a non-recursive calculus such as GOTO are both Turing complete. Since all Turing complete calculi are strictly equivalent in their expressive power, all recursive functions can be implemented by the non-recursive Turing-complete calculus.

Unfortunately, I’m unable to find a good, formal definition of GOTO online so here’s one:

A GOTO program is a sequence of commands P executed on a register machine such that P is one of the following:

  • HALT, which halts execution
  • r = r + 1 where r is any register
  • r = r – 1 where r is any register
  • GOTO x where x is a label
  • IF r ≠ 0 GOTO x where r is any register and x is a label
  • A label, followed by any of the above commands.

However, the conversions between recursive and non-recursive functions isn’t always trivial (except by mindless manual re-implementation of the call stack).

For further information see this answer.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • Great answer! However in practice I have great difficulty turing recursive algos into iterative ones. For example I was unable so far to turn the monomorphic typer presented here http://community.topcoder.com/tc?module=Static&d1=match_editorials&d2=srm286 into an iterative algorithm – Nils Aug 21 '11 at 12:23
  • *"strictly equivalent"* - Does that mean something different than just *"equivalent"*? – Kelly Bundy Apr 21 '23 at 10:14
  • @KellyBundy “equivalent” on its own can be a vague term. I could have instead written “mathematically equivalent” or “equivalent under formal rules of computation”. – Konrad Rudolph Apr 21 '23 at 11:01
31

Recursion is implemented as stacks or similar constructs in the actual interpreters or compilers. So you certainly can convert a recursive function to an iterative counterpart because that's how it's always done (if automatically). You'll just be duplicating the compiler's work in an ad-hoc and probably in a very ugly and inefficient manner.

Vinko Vrsalovic
  • 330,807
  • 53
  • 334
  • 373
  • why ugly an inefficient ? I thought it was cleaner (and easier to debug) using loops, in particular for avoiding a stack overflow when the number of recursions is large. – D.L Aug 23 '22 at 17:43
  • No, the compiler uses stack memory, you'd be using heap memory. There is a huge difference: for example preventing stackoverflows; ironic considering the name of the sire we're on. – avighnac Jul 20 '23 at 08:53
14

Basically yes, in essence what you end up having to do is replace method calls (which implicitly push state onto the stack) into explicit stack pushes to remember where the 'previous call' had gotten up to, and then execute the 'called method' instead.

I'd imagine that the combination of a loop, a stack and a state-machine could be used for all scenarios by basically simulating the method calls. Whether or not this is going to be 'better' (either faster, or more efficient in some sense) is not really possible to say in general.

jerryjvl
  • 19,723
  • 7
  • 40
  • 55
13
  • Recursive function execution flow can be represented as a tree.

  • The same logic can be done by a loop, which uses a data-structure to traverse that tree.

  • Depth-first traversal can be done using a stack, breadth-first traversal can be done using a queue.

So, the answer is: yes. Why: https://stackoverflow.com/a/531721/2128327.

Can any recursion be done in a single loop? Yes, because

a Turing machine does everything it does by executing a single loop:

  1. fetch an instruction,
  2. evaluate it,
  3. goto 1.
Community
  • 1
  • 1
Khaled.K
  • 5,828
  • 1
  • 33
  • 51
11

Yes, using explicitly a stack (but recursion is far more pleasant to read, IMHO).

nbro
  • 15,395
  • 32
  • 113
  • 196
dfa
  • 114,442
  • 31
  • 189
  • 228
7

Yes, it's always possible to write a non-recursive version. The trivial solution is to use a stack data structure and simulate the recursive execution.

Heinzi
  • 167,459
  • 57
  • 363
  • 519
  • Which either defeats the purpose if your stack data structure is allocated on the stack, or takes way longer if it's allocated on the heap, no? That sounds trivial but inefficient to me. – conradkleinespel Sep 18 '14 at 22:11
  • 4
    @conradk In some cases, it's the practical thing to do if you must perform some tree-recursive operation on a problem that is sufficiently large to exhaust the call stack; heap memory is typically much more plentiful. – jamesdlin Dec 27 '16 at 23:39
4

In principle it is always possible to remove recursion and replace it with iteration in a language that has infinite state both for data structures and for the call stack. This is a basic consequence of the Church-Turing thesis.

Given an actual programming language, the answer is not as obvious. The problem is that it is quite possible to have a language where the amount of memory that can be allocated in the program is limited but where the amount of call stack that can be used is unbounded (32-bit C where the address of stack variables is not accessible). In this case, recursion is more powerful simply because it has more memory it can use; there is not enough explicitly allocatable memory to emulate the call stack. For a detailed discussion on this, see this discussion.

Zayenz
  • 1,914
  • 12
  • 11
3

All computable functions can be computed by Turing Machines and hence the recursive systems and Turing machines (iterative systems) are equivalent.

JOBBINE
  • 31
  • 1
1

Recursion is nothing just calling the same function on the stack and once function dies out it is removed from the stack. So one can always use an explicit stack to manage this calling of the same operation using iteration. So, yes all-recursive code can be converted to iteration.

Kautuk Dwivedi
  • 299
  • 2
  • 2
1

Sometimes replacing recursion is much easier than that. Recursion used to be the fashionable thing taught in CS in the 1990's, and so a lot of average developers from that time figured if you solved something with recursion, it was a better solution. So they would use recursion instead of looping backwards to reverse order, or silly things like that. So sometimes removing recursion is a simple "duh, that was obvious" type of exercise.

This is less of a problem now, as the fashion has shifted towards other technologies.

Matthias Wandel
  • 6,383
  • 10
  • 33
  • 31
0

I'd say yes - a function call is nothing but a goto and a stack operation (roughly speaking). All you need to do is imitate the stack that's built while invoking functions and do something similar as a goto (you may imitate gotos with languages that don't explicitly have this keyword too).

sfussenegger
  • 35,575
  • 15
  • 95
  • 119
0

Have a look at the following entries on wikipedia, you can use them as a starting point to find a complete answer to your question.

Follows a paragraph that may give you some hint on where to start:

Solving a recurrence relation means obtaining a closed-form solution: a non-recursive function of n.

Also have a look at the last paragraph of this entry.

Alberto Zaccagni
  • 30,779
  • 11
  • 72
  • 106
0

It is possible to convert any recursive algorithm to a non-recursive one, but often the logic is much more complex and doing so requires the use of a stack. In fact, recursion itself uses a stack: the function stack.

More Details: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions

Teoman shipahi
  • 47,454
  • 15
  • 134
  • 158
0

Removing recursion is a complex problem and is feasible under well defined circumstances.

The below cases are among the easy:

Nick Dandoulakis
  • 42,588
  • 16
  • 104
  • 136
0

Appart from the explicit stack, another pattern for converting recursion into iteration is with the use of a trampoline.

Here, the functions either return the final result, or a closure of the function call that it would otherwise have performed. Then, the initiating (trampolining) function keep invoking the closures returned until the final result is reached.

This approach works for mutually recursive functions, but I'm afraid it only works for tail-calls.

http://en.wikipedia.org/wiki/Trampoline_(computers)

Chris Vest
  • 8,642
  • 3
  • 35
  • 43
-2

tazzego, recursion means that a function will call itself whether you like it or not. When people are talking about whether or not things can be done without recursion, they mean this and you cannot say "no, that is not true, because I do not agree with the definition of recursion" as a valid statement.

With that in mind, just about everything else you say is nonsense. The only other thing that you say that is not nonsense is the idea that you cannot imagine programming without a callstack. That is something that had been done for decades until using a callstack became popular. Old versions of FORTRAN lacked a callstack and they worked just fine.

By the way, there exist Turing-complete languages that only implement recursion (e.g. SML) as a means of looping. There also exist Turing-complete languages that only implement iteration as a means of looping (e.g. FORTRAN IV). The Church-Turing thesis proves that anything possible in a recursion-only languages can be done in a non-recursive language and vica-versa by the fact that they both have the property of turing-completeness.

  • The Church-Turing thesis is an _informal_ hypothesis that anything that can be computed by any kind of algorithm, including kinds that haven't been discovered or invented yet, can be computed by a Turing machine. Since there is no formal definition of "any kind of algorithm", the C-T thesis is not a mathematical theorem. What _is_ a theorem (proven by Church and Turing) is the equivalence between Turing machines and Church's [_lambda calculus_](https://en.wikipedia.org/wiki/Lambda_calculus). – Ron Inbar Apr 03 '21 at 17:58
-3

Here is an iterative algorithm:

def howmany(x,y)
  a = {}
  for n in (0..x+y)
    for m in (0..n)
      a[[m,n-m]] = if m==0 or n-m==0 then 1 else a[[m-1,n-m]] + a[[m,n-m-1]] end
    end
  end
  return a[[x,y]]
end
Jules
  • 6,318
  • 2
  • 29
  • 40