Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swapping Elements in Python Iteratively

I am having trouble understanding what's happening behind the scenes for this simple code snippet:

def changeArray(arr):
     for i in range(len(arr)):
         arr[i], arr[arr[i] - 1] = arr[arr[i] - 1], arr[i]
         print(arr)
     return(arr)

The code assumes the array has as its elements the integers from 1 to n. The output for the given code when the input is [1,3,4,2] is:

[1, 3, 4, 2]
[1, 4, 4, 3]
[1, 4, 4, 3]
[1, 4, 4, 3]
Out[8]: [1, 4, 4, 3]

while I was expecting it to print and return this:

[1, 3, 4, 2]
[1, 4, 3, 2]
[1, 4, 3, 2]
[1, 2, 3, 4]
Out[8]: [1, 2, 3, 4]

Why are the values changing at all when the code is only swapping elements?


Edit:

It turns out, changing the swapping order fixes the problem:

def changeArray(arr):
     for i in range(len(arr)):
         arr[arr[i]-1], arr[i] = arr[i], arr[arr[i]-1]
         print(arr)
     return(arr)

This gives the following output:

[1, 3, 4, 2]
[1, 4, 3, 2]
[1, 4, 3, 2]
[1, 2, 3, 4]
Out[8]: [1, 2, 3, 4]

How did changing the order do the swapping as expected, and the reverse did something else entirely?

like image 805
dexteran Avatar asked Oct 17 '22 20:10

dexteran


1 Answers

In general, you shouldn't use the object you're mutating to specify the target positions you want to replace, or it gets very confusing.

When you write this:

 arr[i], arr[arr[i] - 1] = arr[arr[i] - 1], arr[i]

It's roughly equivalent to:

tup = arr[arr[i] - 1], arr[i]
x, y = tup
arr.__setitem__(i, x)
arr.__setitem__(arr[i] - 1, y)

(Full details for how to translate this are in the reference docs, but hopefully the inuitive idea is a lot simpler.)

Which should make it clear why you're getting the results you are. And also why all of the following do what you want:

x = arr[i] - 1
arr[i], arr[x] = arr[x], arr[i]

arr[arr[i] - 1], arr[i] = arr[i], arr[arr[i] - 1]

def swap(x, y):
    arr[x], arr[y] = arr[y], arr[x]
swap(i, arr[i] - 1)

I think the first one is the simplest (the second one looks simple, but only misleadingly so).

like image 154
abarnert Avatar answered Oct 21 '22 04:10

abarnert