2
# left rotate using slicing
def leftRotate(arr, k, n):
    arr = arr[k:] + arr[:k]
    print(arr)

arr = [1, 2, 3, 4, 5, 6, 7] 
leftRotate(arr, 2, 7) 
print(arr)

Result:

[3, 4, 5, 6, 7, 1, 2]
[1, 2, 3, 4, 5, 6, 7]

When I print the array outside the function it is not rotated anymore and remains how it originally was. Can someone help me understand this?

wim
  • 338,267
  • 99
  • 616
  • 750
PyRookie
  • 75
  • 2
  • 4
  • 3
    yes you can, but you aren't modifying the list in your function, you simply assign to a local variable `arr` and then the function terminates – juanpa.arrivillaga Nov 19 '18 at 15:42

4 Answers4

6

Yes, you can change a list from within a function, but you need to use the correct syntax. As you've seen already, this is not the correct way:

def leftRotate(arr, k, n):
    arr = arr[k:] + arr[:k]

I will try to explain why this did not work, and hope to give you a better intuition about what really happens. Inside the scope of the function shown above, there are 3 local variables: arr, k, and n. The right-hand side operations arr[k:] + arr[:k] creates a new list object, without modifying the original list, and this resulting object is bound to the local variable name arr. This does not modify the original object, because such assignment statements in Python are never mutating objects. They will only bind a name in a namespace. Think of it as if you're taking the nametag "arr" off of the old list object, which was passed in as argument, and sticking it on the new list object which was just created. The old list object is not modified by such an operation, only the local namespace is modified - the old list object becomes "anonymous" and is no longer reachable in this scope.

The solution is to use a different kind of assignment statement, a slice assignment, which does mutate:

def leftRotate(arr, k, n):
    arr[:] = arr[k:] + arr[:k]

As a final note, there is a list-like data structure in stdlib which provides more efficient rotation operations (at the cost of less-efficient indexing into the middle of the collection). If you're interested in this, read the docs on the collections.deque.

wim
  • 338,267
  • 99
  • 616
  • 750
2

The problem is list slicing is not being applied in place. Effectively a new list is created and assigned to a variable arr scoped to leftRotate, i.e. it can can be accessed within your function only. A method which does work in place will work as expected:

def rev_sort(arr, k, n):
    arr.sort(reverse=True)
    print(arr)

arr = [1, 2, 3, 4, 5, 6, 7] 
rev_sort(arr, 2, 7) 

print(arr)

[7, 6, 5, 4, 3, 2, 1]
[7, 6, 5, 4, 3, 2, 1]

In your example, you can have your function return a list and assign it to arr:

def leftRotate(arr, k, n):
    arr = arr[k:]+arr[:k]
    print(arr)
    return arr

arr = [1, 2, 3, 4, 5, 6, 7] 
arr = leftRotate(arr, 2, 7) 
print(arr)

[3, 4, 5, 6, 7, 1, 2]
[3, 4, 5, 6, 7, 1, 2]
jpp
  • 159,742
  • 34
  • 281
  • 339
2

Your problem is because you can't change a variable inside a python function because of the scope. Read this for more info.

But resuming, you need to either return arr and assign it outside. Like this:

#left rotate using slicing
def leftRotate(arr, k, n):
    arr=arr[k:]+arr[:k]
    return arr

arr = [1, 2, 3, 4, 5, 6, 7] 
arr = leftRotate(arr, 2, 7) 
print arr

Or if you would like, you could make arr a global. (Check this for more info on that). (Don't recommend this last one, but exists)

arr = [1, 2, 3, 4, 5, 6, 7]

#left rotate using slicing
def leftRotate( k, n):
    global arr
    arr=arr[k:]+arr[:k]

leftRotate( 2, 7) 
print arr

Hope it helped :)

Marcos Jota
  • 109
  • 4
-5

There are a lot of really complicated answers. Here's the "for dummies" version:

  • You are passing arr into leftRotate()
  • For nearly all purposes, you can think of this as creating another variable, also called arr which leftRotate() works on. leftRotate()'s arr is not the same as the arr you are passing in to leftRotate(). It is a copy (technically it's not a copy until you assign arr to something else, but close enough for these purposes).
  • You're not getting your modified arr back out of leftRotate() again.

You can solve this in two ways:

  • Define arr outside of leftRotate(), and don't pass arr in. I'll call this the "global" approach. Not recommended unless you have a very good reason.
  • Use return arr after your function completes. Essentially, return 'x' means leftRotate() == 'x'. In 99.99999% of cases, this is what you want.

Therefore, in your example, what you really want is this:

#left rotate using slicing
def leftRotate(arr, k, n):
    arr=arr[k:]+arr[:k] # not sure this is right, but once you get the return working, it should be easy to debug
    # print arr # changed
    return arr

arr = [1, 2, 3, 4, 5, 6, 7] 
# leftRotate(arr, 2, 7) # changed
arr = leftRotate(arr, 2, 7) 
print arr
NotAnAmbiTurner
  • 2,553
  • 2
  • 21
  • 44
  • Are you sure that passing `arr` into `leftRotate` creates a copy? – wim Nov 19 '18 at 20:36
  • 2
    Your assumption that `arr` passed to the function is a copy is incorrect. The part that fails is that assignment to `arr` inside the function just assigns to a new, local name, not affecting the original `arr`. It *is* possible to update the original passed-in `arr` by using slice assignment, as shown in @wim 's answer. – PaulMcG Nov 19 '18 at 20:46
  • I said it was the "for dummies" version. That means it's meant to be more easily understandable, if not precisely correct. We're not all professional coders (and OP prob isn't). I can tell you it's not a pointer/assignment. "Copy" to most people is the same as "local variable outside of scope" or whatever. – NotAnAmbiTurner Nov 19 '18 at 21:06