I've started reading the book Systematic Program Design: From Clarity to Efficiency few days ago. Chapter 4 talks about a systematic method to convert any recursive algorithm into its counterpart iterative. It seems this is a really powerful general method but I'm struggling quite a lot to understand how it works.
After reading a few articles talking about recursion removal using custom stacks, it feels like this proposed method would produce a much more readable, optimized and compact output.
Recursive algorithms in Python where I want to apply the method
#NS: lcs and knap are using implicit variables (i.e.: defined globally), so they won't
#work directly
# n>=0
def fac(n):
    if n==0:
        return 1
    else:
        return n*fac(n-1)
# n>=0
def fib(n):
    if n==0:
        return 0
    elif n==1:
        return 1
    else:
        return fib(n-1)+fib(n-2)
# k>=0, k<=n
def bin(n,k):
    if k==0 or k==n:
        return 1
    else:
        return bin(n-1,k-1)+bin(n-1,k)
# i>=0, j>=0
def lcs(i,j):
    if i==0 or j==0:
        return 0
    elif x[i]==y[j]:
        return lcs(i-1,j-1)+1
    else:
        return max(lcs(i,j-1),lcs(i-1,j))
# i>=0, u>=0,  for all i in 0..n-1 w[i]>0
def knap(i,u):
    if i==0 or u==0:
        return 0
    elif w[i]>u:
        return knap(i-1,u)
    else:
        return max(v[i]+knap(i-1,u-w[i]), knap(i-1,u))
# i>=0, n>=0
def ack(i,n):
    if i==0:
        return n+1
    elif n==0:
        return ack(i-1,1)
    else:
        return ack(i-1,ack(i,n-1))
Step Iterate: Determine minimum increments, transform recursion into iteration
The Section 4.2.1 the book talks about determining the appropriate increment:
1) All possible recursive calls
    fact(n)   => {n-1}
    fib(n)    => {fib(n-1), fib(n-2)}
    bin(n,k)  => {bin(n-1,k-1),bin(n-1,k)}
    lcs(i,j)  => {lcs(i-1,j-1),lcs(i,j-1),lcs(i-1,j)}
    knap(i,u) => {knap(i-1,u),knap(i-1,u-w[i])}
    ack(i,n)  => {ack(i-1,1),ack(i-1,ack(i,n-1)), ack(i,n-1)}
2) Decrement operation
    fact(n)   => n-1
    fib(n)    => n-1
    bin(n,k)  => [n-1,k]
    lcs(i,j)  => [i-1,j]
    knap(i,u) => [i-1,u]
    ack(i,n)  => [i,n-1]
3) Minimum increment operation
    fact(n)   => next(n) = n+1
    fib(n)    => next(n) = n+1
    bin(n,k)  => next(n,k) = [n+1,k]
    lcs(i,j)  => next(i,j) = [i+1,j]
    knap(i,u) => next(i,u) = [i+1,u]
    ack(i,n)  => next(i,n) = [i,n+1]
Section 4.2.2 talks about forming the optimized program:
Recursive
---------
def fExtOpt(x):
    if base_cond(x) then fExt0(x )       -- Base case
    else let rExt := fExtOpt(prev(x)) in -- Recursion
        f Ext’(prev(x),rExt)              -- Incremental computation
Iterative
---------
def fExtOpt(x):
    if base_cond(x): return fExt0(x)                    -- Base case
    x1 := init_arg; rExt := fExt0(x1)                   -- Initialization
    while x1 != x:                                      -- Iteration
        x1 := next(x1); rExt := fExt’(prev(x1),rExt)    -- Incremental comp
    return rExt
How do I create {fibExtOpt,binExtOpt,lcsExtOpt,knapExtOpt,ackExtOpt} in Python?
Additional material about this topic can be found in one of the papers of the main author of the method, Y. Annie Liu, Professor.
 
     
    