Search code examples
pythonlistrecursionmemory-managementbubble-sort

How does passing a list as an argument work in Python3?


I was trying to code Bubble Sort using recursion, when I ran into this problem.

def bubble_sort_recur(a):
    print(a)
    if len(a)==1:
        return
    for i in range(len(a)-1):
        if a[i] > a[i+1] :
            a[i]=a[i]^a[i+1] # swap using XOR
            a[i+1]=a[i]^a[i+1]
            a[i]=a[i]^a[i+1]
    print(a) # shows the changes made in the current call
    bubble_sort_recur(a[:-1])

b=[1,6,1,66,3,32,21,33,1] # initial list
bubble_sort_recur(b) # function call

I think that python uses pass by Object reference for objects(lists). The output of the recursion also was expected, and as follows:

[1, 6, 1, 66, 3, 32, 21, 33, 1]
[1, 1, 6, 3, 32, 21, 33, 1, 66] # 66 bubbles up
[1, 1, 6, 3, 32, 21, 33, 1]
[1, 1, 3, 6, 21, 32, 1, 33]
[1, 1, 3, 6, 21, 32, 1]
[1, 1, 3, 6, 21, 1, 32]
[1, 1, 3, 6, 21, 1]
[1, 1, 3, 6, 1, 21]
[1, 1, 3, 6, 1]
[1, 1, 3, 1, 6]
[1, 1, 3, 1]
[1, 1, 1, 3]
[1, 1, 1]
[1, 1, 1]
[1, 1]
[1, 1]
[1]

One can see that the third '1' bubbles down to the 2nd index as expected. But when I do print(b) I get [1, 1, 6, 3, 32, 21, 33, 1, 66] which is neither the sorted list nor the original list(b=[1,6,1,66,3,32,21,33,1]). Could you please explain the reason for getting such a mixed behaviour? Something I tried to explore the working is as follows:

def func(a):
    a[0]='only one altered'
def func2(b):
    a=['this','is', 'completely', 'new']
a=[1,'initial',2, 'array']
func(a) # this changes the list
print(a)
func2(a) # this does not change the list 'a' , in any way.
print(a)
>> ['only one altered', 'initial', 2, 'array']
   ['only one altered', 'initial', 2, 'array']

I notice that , the actual list gets altered when I change the value of the list inside of the function by using the index. Otherwise the list remains unaffected. Any help appreciated.


Solution

  • Your function does not return a value and relies on the list being mutable for it to be changed in place. However, you then call recursion with a slice of the the current list, which makes a copy (and therefore a new list), so the list b is no longer being referenced.

    You can verify this by adding a print(b) line before recursion.

    def bubble_sort_recur(a):
        print(a)
        if len(a)==1:
            return
        for i in range(len(a)-1):
            if a[i] > a[i+1] :
                a[i]=a[i]^a[i+1] # swap using XOR
                a[i+1]=a[i]^a[i+1]
                a[i]=a[i]^a[i+1]
        print(a) # shows the changes made in the current call
        print(b)
        print()
        bubble_sort_recur(a[:-1])
    
    b=[1,6,1,66,3,32,21,33,1] # initial list
    bubble_sort_recur(b) # function call
    

    Output

    [1, 6, 1, 66, 3, 32, 21, 33, 1]
    [1, 1, 6, 3, 32, 21, 33, 1, 66]
    [1, 1, 6, 3, 32, 21, 33, 1, 66] # first call changed b
    
    [1, 1, 6, 3, 32, 21, 33, 1]
    [1, 1, 3, 6, 21, 32, 1, 33]
    [1, 1, 6, 3, 32, 21, 33, 1, 66] # none of the recursions changed b
    
    [1, 1, 3, 6, 21, 32, 1]
    [1, 1, 3, 6, 21, 1, 32]
    [1, 1, 6, 3, 32, 21, 33, 1, 66]
    
    [1, 1, 3, 6, 21, 1]
    [1, 1, 3, 6, 1, 21]
    [1, 1, 6, 3, 32, 21, 33, 1, 66]
    
    [1, 1, 3, 6, 1]
    [1, 1, 3, 1, 6]
    [1, 1, 6, 3, 32, 21, 33, 1, 66]
    
    [1, 1, 3, 1]
    [1, 1, 1, 3]
    [1, 1, 6, 3, 32, 21, 33, 1, 66]
    
    [1, 1, 1]
    [1, 1, 1]
    [1, 1, 6, 3, 32, 21, 33, 1, 66]
    
    [1, 1]
    [1, 1]
    [1, 1, 6, 3, 32, 21, 33, 1, 66]
    
    [1]
    

    One solution would be to include a return call.

    def bubble_sort_recur(a):
        if len(a) > 1:
            for i in range(len(a)-1):
                if a[i] > a[i+1] :
                    a[i], a[i+1] = a[i+1], a[i] # more pythonic syntax
                    # or
                    # a[i:i+2] = a[i+1:i-1:-1]
                    # or your original code
                    # a[i]=a[i]^a[i+1] # swap using XOR
                    # a[i+1]=a[i]^a[i+1]
                    # a[i]=a[i]^a[i+1]
            print(a)
            a[:-1] = bubble_sort_recur(a[:-1]) # catch the result of recursion
        return a
    
    b = [1,6,1,66,3,32,21,33,1] # initial list
    b = bubble_sort_recur(b) # function call
    
    [1, 1, 6, 3, 32, 21, 33, 1, 66]
    [1, 1, 3, 6, 21, 32, 1, 33]
    [1, 1, 3, 6, 21, 1, 32]
    [1, 1, 3, 6, 1, 21]
    [1, 1, 3, 1, 6]
    [1, 1, 1, 3]
    [1, 1, 1]
    [1, 1]
    
    >>> print(b)
    [1, 1, 1, 3, 6, 21, 32, 33, 66]