0

The code is passed an array. My understanding is this passing is done by reference. I want the function to recursively divide the last remaining half of the list in two and set each value that it was split at to zero. The change to zero is happens in the array but when I call print a at the end I get the original array.

What am i doing wrong?

a = range(10)

def listreduction(array):
    if len(array) == 1:
        array[0] = 0
        return

    split = len(array)/2
    array[split] = 0

    return listreduction(array[split:])

listreduction(a)
print a

The current output is

[0, 1, 2, 3, 4, 0, 6, 7, 8, 9]

The should be more zeros to the right of the second one

MattTheSnake
  • 107
  • 1
  • 7
  • 1
    What's the output you're expecting to get? – BPL Sep 17 '16 at 15:05
  • Ranges != to arrays btw. Since a range is immutable, it must be being copied as an array somewhere within the function, thus the problem. Always just explicitly return; don't rely on side effects. I actually can't remember if Python is pass-by-reference. I seem to recall it being much more complicated than that. – Carcigenicate Sep 17 '16 at 15:07
  • When you use recursion, you do not pass actual list to the method. That is the problem. – scriptmonster Sep 17 '16 at 15:10
  • @Carcigenicate `Since a range is immutable` -- wait, what? It's clearly Python 2, and range just returns a perfectly mutable list there. – Pavel Gurkov Sep 17 '16 at 15:12
  • @PavelGurkov Really? I thought a range was always just a class of 3 values. OK, well. My bad. – Carcigenicate Sep 17 '16 at 15:13
  • 1
    @Carcigenicate is it possible your thinking of xrange in python 2.x. xrange generates an xrange object that generates values when the index is requested. This saves a lot of memory space. In python 3.x xrange was renamed range. In python 2.x range returns a list. I think the problem is with the recursive calls to sublists. There must be a copy generated when you don't pass the entire list to the next function – MattTheSnake Sep 17 '16 at 15:19

4 Answers4

2

A slice creates a new list. If you want to do this recursively, you'll have to pass the index where the function is supposed to work on the list, not the actual slice.

Pedro Werneck
  • 40,902
  • 7
  • 64
  • 85
1

Since you use recursion, the slice operation in the argument will create new list instance which is different than you instance. That's the reason.

You can change your code as following:

a = range(10)

def list_reduction(array, position=0):
    if len(array) -1 <= position:
        return

    split = position + (len(array) - position) / 2
    array[split] = 0
    return list_reduction(array, split)

list_reduction(a)
print a

The output is:

[0, 1, 2, 3, 4, 0, 6, 0, 0, 0]
scriptmonster
  • 2,741
  • 21
  • 29
1

This is probably what you want.

a = range(1, 10)

def list_reduction(l, prev_split_pos=None):
    split_pos = (len(l) + prev_split_pos) / 2 if prev_split_pos else len(l) / 2
    if split_pos == prev_split_pos:
        return
    l[split_pos] = 0
    return list_reduction(l, split_pos)

list_reduction(a)
print a

So, to your code. Everytime you do a list slice, you actually generate a new list, which is not at all connected to the old one. This is why you don't see any mutations to it except the first one.

Pavel Gurkov
  • 737
  • 5
  • 14
  • Thank you! This is exactly what I was looking for. I started to suspect my list passing was creating copies instead of only operating on the sublist but still in place. – MattTheSnake Sep 17 '16 at 15:35
  • @MattTheSnake You're welcome. As a side note, check out this great question on passing by reference/value in Python to get a better grasp on it: http://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference – Pavel Gurkov Sep 17 '16 at 15:37
  • That definitely helps clear things up. Thanks again for the help – MattTheSnake Sep 17 '16 at 15:51
1

In Python argumnet passing is different from other conventional programming language. arguments are passed by object reference. And the whether the referred object will be modifed or not it depends on two things

  • Whether the variable is mutable or immutable. In your case range will create a list so its a mutable object. That implies that if you update array it should also update a.
  • Operation. = operation will always create new object reference. So even in your case array is mutable but since you are doing assignment operation It will create a new object reference.

Following example should clear up the things for you.

Example 1

  >>> a = [1,2]
    def fun1(array):
        array= array + [3]
        print "var array = %s" %array

    fun1(a)
    print "var a = %s" %a

Output

var array = [1, 2, 3]
var a = [1, 2]

Example 2

a = [1,2]
def fun1(array):
    array.append(3)
    print "var array = %s" %array

fun1(a)
print "var a = %s" %a

Output

var array = [1, 2, 3]
var a = [1, 2,3]
saurabh baid
  • 1,819
  • 1
  • 14
  • 26