Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python, Make an iterative function into a recursive function

I've created an iterative function which outputs 4 3 2 1 0 1 2 3 4.

def bounce2(n):
    s = n
    for i in range(n):
        print(n)
        n = n-1

    if n <= 0:
        for i in range(s+1):
            print(-n)
            n = n-1
    return
bounce2(4)

If I want a recursive function that does the exact same thing, how should I think?

like image 583
Krillex Avatar asked Sep 25 '18 12:09

Krillex


People also ask

How can I convert a recursive function to iteration?

All recursive functions can be converted to iteration by simulating the stack to store state. However, recursion is usually slower and uses more memory because of the overhead of creating and maintaining stack frames. This doesn't mean never use recursion though. Recursion is the better choi BS in CS and 12 years of work experience in programming.

How does recursion work in Python?

It works like the loops we described before, but sometimes it the situation is better to use recursion than loops. Every recursive function has two components: a base case and a recursive step.

How do you write a factorial function using recursion?

The recursive definition can be written: The base case is n = 1 which is trivial to compute: f ( 1) = 1. In the recursive step, n is multiplied by the result of a recursive call to the factorial of n − 1. TRY IT! Write the factorial function using recursion.

Is iteration faster than recursion in Python?

Since Python does not store anything about previous iteration steps, iteration is quite faster and memory-efficient than recursion. In practice, almost all iterations can be performed by recursions and vice-versa. Some tasks can be executed by recursion simpler than iteration due to repeatedly calling the same function.


2 Answers

Try this:

def bounce(n):
    if n >= 0:
        print(n)
        bounce(n - 1)

        if n:
            print(n)

bounce(4)

the output will be: 4 3 2 1 0 1 2 3 4

like image 186
Mehrdad Pedramfar Avatar answered Nov 14 '22 22:11

Mehrdad Pedramfar


Expected output:

4 3 2 1 0 1 2 3 4

Let me put it into a diagram:

4
  3
    2
      1
        0
      1
    2
  3
4

Let's put that into code:

def bounce(n):
    print(n)
    if n:
        bounce(n - 1)
        print(n)

Or I can see it as a tree traversal - down and up (well, the tree is pretty linear in this case):

↓
  4
↓ | ↑
  3
↓ | ↑
  2
↓ | ↑
  1
↓ | ↑
  0

There are multiple ways how to do a tree traversal, I would pick DFS here - depth-first search.

DFS pseudocode:

procedure DFS(G,v):
    label v as discovered
    for all edges from v to w in G.adjacentEdges(v) do
        if vertex w is not labeled as discovered then
            recursively call DFS(G,w)

Actually it's designed for graphs, not just trees; for trees we don't have to do the "label as discovered" part.

Translate this DFS to Python:

def dfs(node):
    for child_node in node:
        dfs(child_node)

In the 4 3 2 1 0 case we don't need the for becase there is exactly one or zero child nodes - n - 1 as long as n > 0:

def our_dfs(n):
    if n > 0:
        child_node = n - 1
        our_dfs(child_node)

But that does just the traversal and nothing really useful yet. Let's inject our "business logic":

def bounce(n):
    # stuff that happens before we go down
    print(n)
    # descend
    if n > 0:
        child_node = n - 1
        bounce(child_node)
    # stuff that happens after we are back from processing the subtree
    if n > 0:
        print(n)

Because we believe in good craftsmanship and we want to produce clean code (OK I am starting to be joking now) we want functions that do only one thing - one function for DFS, one function that represents our tree, separate function(s) for our business logic:

def dfs(node, child_factory, before_stuff, after_stuff):
    before_stuff(node)
    for child_node in get_child_nodes(node):
        dfs(child_node, child_factory, before_stuff, after_stuff)
    after_stuff(node)

def get_child_nodes(n):
    if n:
        yield n - 1

def print_before(node):
    print(node)

def print_after(node):
    if node:
        print(node)

def bounce(n):
    dfs(n, get_child_nodes, print_before, print_after)

bounce(4)

Maybe we could make the dfs function a bit simpler by using nested function:

def dfs(node, child_factory, before_stuff, after_stuff):
    def f(node):
        before_stuff(node)
        for child_node in get_child_nodes(node):
            f(child_node)
        after_stuff(node)
    f(node)

Hey, lookign at it, I have one more idea... We could modify this to a function that returns a function (closure) that can do DFS:

def make_dfs(child_factory, before_stuff, after_stuff):
    def dfs(node):
        before_stuff(node)
        for child_node in get_child_nodes(node):
            dfs(child_node)
        after_stuff(node)
    return dfs

So the bounce program now becomes:

def get_child_nodes(n):
    if n:
        yield n - 1

def print_before(node):
    print(node)

def print_after(node):
    if node:
        print(node)

def make_dfs(child_factory, before_stuff, after_stuff):
    def dfs(node):
        before_stuff(node)
        for child_node in get_child_nodes(node):
            dfs(child_node)
        after_stuff(node)
    return dfs

bounce = make_dfs(get_child_nodes, print_before, print_after)

bounce(4)

So, what's so cool about this solution? (note: still joking, partially) You know, Python has a recursion limit. There is a finite number of how many function calls can be nested and this number is pretty low. That's a big downside (sometimes even security concern) of processing unknown inputs using recursive functions. So what now? Well, just replace the implementation of make_dfs with something stack-based (see that DFS Wikipedia page) instead of recursion. Done. You don't have to touch anything else.

like image 43
Messa Avatar answered Nov 14 '22 22:11

Messa