-3

The code is quite easy but its returning 'None', i do not know why. Can someone please correct my code.

def rev(x):
    global y
    if len(x)==0:
        return y

    else:
        y=y+[x[-1]]
        x.pop(-1) #Remove last item from list
        rev(x)

z=[1,2,3]
y=[]
print(rev(z))
E_net4
  • 27,810
  • 13
  • 101
  • 139
  • 1
    because you don't return anything for the case where `len(x) != 0` and the implicit return in python is `None`. – Mad Physicist Dec 23 '21 at 14:39
  • Your else clause doesn't return anything. – Steven Rumbalski Dec 23 '21 at 14:39
  • Can you correct the code? – Azhan Ahmed Dec 23 '21 at 14:44
  • If you want to reverse a list you can also use `l = [1, 2, 3, 4]` -> `l[::-1]` -> `l = [4, 3, 2, 1]`. – 3dSpatialUser Dec 23 '21 at 14:55
  • Notwithstanding previous comments, I wonder why you're doing this. Is this a homework exercise in recursion or do you just want to reverse a list without using traditional slicing techniques? – DarkKnight Dec 23 '21 at 15:17
  • It's not homework but practice question to understabd recursion. – Azhan Ahmed Dec 23 '21 at 15:34
  • Normally I'd say "I recommend not using `global` especially if you're beginning" but given that your goal is to better understand recursion, I'm going to say: I **very strongly** recommend not using `global`! – Stef Dec 23 '21 at 15:44
  • See also the myriad of duplicates for "python recursive function returning None", for instance: [Recursive function returning None in python?](https://stackoverflow.com/questions/19215141/recursive-function-returning-none-in-python) – Stef Dec 23 '21 at 15:51

2 Answers2

2

Thank you but I'm supposed to use recursion...

Recursion is a functional heritage and so using it with functional style yields the best results. This means avoiding things like mutation, variable reassignments, and other side effects.

  1. if the input t is empty, return the empty result
  2. (inductive) t has at least one element. prepend the result of the sub-problem rev(t[1:0) to a singleton list of the first element, [t[0]]
def rev(t):
  if not t:
    return []            # 1. empty t
  else:
    rev(t[1:]) + [t[0]]  # 2. at least one element

A function without side effects will always return the same result for the same input. This allows us to substitute and function call for its output and easily visualize its computational process -

rev([1,2,3,4,5])
== rev([2,3,4,5]) + [1]
== rev([3,4,5]) + [2] + [1]
== rev([4,5]) + [3] + [2] + [1]
== rev([5]) + [4] + [3] + [2] + [1]
== rev([]) [5] + [4] + [3] + [2] + [1]
== [] + [5] + [4] + [3] + [2] + [1]
== [5] + [4] + [3] + [2] + [1]
== [5,4] + [3] + [2] + [1]
== [5,4,3] + [2] + [1]
== [5,4,3,2] + [1]
== [5,4,3,2,1]

Notice the pyramid shape of the pending computations. As the input grows, the amount of singleton lists waiting to be combined grows. If t were significantly large, this would result in a stack overflow.

Let's see how one small adjustment can make a big impact on the process -

def rev(t, r = []):
  if not t:
    return r               # 1. empty t, return r
  else:
    rev(t[1:], [t[0]] + r) # 2. at least one element, prepend to r

Instead of leaving pending computations for us to come back to, we introduce a second argument with a default value. Notice how the visualized process below stays linear and flat instead of growing like a pyramid -

rev([1,2,3,4,5])
== rev([2,3,4,5], [1])
== rev([3,4,5], [2,1])
== rev([4,5], [3,2,1])
== rev([5], [4,3,2,1])
== rev([], [5,4,3,2,1])
== [5,4,3,2,1]

When the recursive call is the last call of the function, it is said to be tail recursive. Being that recursion is the natural looping mechanism in functional languages, the compiler will detect these tail calls and effectively prevent the stack from growing, referred to as tail-call elimination.

myinput = "abcdefghijklmnopqrstuvwxyz"
print("".join(rev(myinput)))
print("".join(rev(myinput * 100)))
zyxwvutsrqponmlkjihgfedcba
RecursionError: maximum recursion depth exceeded

So why did Python fail to compute rev(myinput * 100)? While Python supports many functional features such as modules and first-class functions, it does not offer tail-call elimination and so our rev function above still overflows the stack. Combined with its relatively small stack size, this fact makes Python a terrific language to actually learn about recursion. In understanding its limitations, you will know what it means to write efficient algorithms and even come to understand how stack-safe infinite recursion is possible in other languages.

For example, here's one technique that enables infinite recursion by using Python's inspect module which allows us to compare stack frame data. The recursive function is converted to a while loop and when a tail-call is no longer detected, the final result is returned. Reza Bagheri offers detailed explanation of this technique in this article -

# tail_rec.py
import inspect

def tail_rec(func):
  is_rec = False
  t = []
  def loop(*args):
    nonlocal is_rec
    nonlocal t
    f = inspect.currentframe()
    if f.f_back:
      if f.f_back.f_back:
        if f.f_code == f.f_back.f_back.f_code:
          is_rec = True
          t = args
          return 
    while True:
      try:
        result = func(*args)
      except TypeError:
        raise Exception("function is not tail-recursive")
      if is_rec:
        is_rec = False
        args = t
      else:
        return result 
  return loop

To enable the optimization, we simply add the tail_rec decorator to our function -

from tail_rec import tail_rec    # <- import

@tail_rec                        # <- enable tail call elimination
def rev(t, r = []):
  if not t:
    return r
  else:
    return rev(t[1:], [t[0]] + r)

And suddenly Python recursion limits have vanished into nothingness -

myinput = "abcdefghijklmnopqrstuvwxyz"
print("".join(rev(myinput * 100)))
zyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcba
Mulan
  • 129,518
  • 31
  • 228
  • 259
  • I upvoted because I agree with everything said in this answer and it is very clear and well-explained. But I would note python is a terribly inappropriate language to learn recursion. This function `rev` is going to have a quadratic complexity because of every `+` operation rebuilding the list entirely to produce a new list. – Stef Dec 23 '21 at 15:48
  • 2
    A learner must first be able to think/visualize recursive processes before they can also make space/time analyses on them. The fact that the naive implementation has quadratic complexity is itself the gateway lesson to deepening your understanding about recursion. I updated my answer to address this. – Mulan Dec 23 '21 at 16:51
  • Perhaps I shouldn't have said anything! The answer was already great; now it's very long, which means it's harder for someone reading it to remember the important message out of it ;-) – Stef Dec 23 '21 at 16:56
  • Also now you have a function with a mutable default argument, `r = []`, which causes issues of its own in python. – Stef Dec 23 '21 at 16:57
  • 1
    Thanks for your feedback, Stef. It's difficult to teach functional style because its full benefits are only realized when applied to an entire system. Looking at it through a pinhole results in many conversations like this one. Default arguments like `r = []` do not _cause issues_. It's **mutation** of default arguments that causes issues but true functional style avoids any observable mutations. Issues into nothingness. – Mulan Dec 23 '21 at 17:01
0

Here's an example of how you could reverse the contents of a list in situ without slicing or recursion (not that you'd ever want to do it this way):

def rev(x):
    i = 0
    j = len(x)
    while (j := j - 1) > i:
        x[i], x[j] = x[j], x[i]
        i += 1
    return x

print(rev([1,2,3,4,5]))
DarkKnight
  • 19,739
  • 3
  • 6
  • 22
  • Thank you but I'm supposed to use recursion, this is supposed to be done for end of chapter exercises. – Azhan Ahmed Dec 23 '21 at 15:38
  • I don't think `while (j := j - 1) > i:` is a very pedagogical advice ;-) I suggest separating the condition `j - 1 > i` and the assignment `j -= 1`. – Stef Dec 23 '21 at 15:46