Gal Shahar
Gal Shahar

Reputation: 2785

How is this line being executed in python?

I'm writing a recursive function which swaps pairs in a singly linked list. In the function there's this line which works all fine:

head.next.next, head.next = head, head.next.next

But if I switch the order to be like this:

head.next, head.next.next = head.next.next, head

I get a RecursionError: maximum recursion depth exceeded error.

What is the difference between the two lines? How are they being executed by the interpreter?

Thanks!

Upvotes: 0

Views: 75

Answers (2)

kaya3
kaya3

Reputation: 51034

The difference is that in the second one, head.next is reassigned before head.next.next is evaluated, so the second assignment target (head.next).next uses the result from the first assignment to head.next.

The excellent Python Tutor tool can be used to visualise what happens when the code is run, which may help you to understand: I ran each piece of code using a list with the numbers 1, 2, 3; the image below shows the initial state of the list:

initial state

Here's the result after the first code; note that I created another reference to the Node(2), otherwise it would be lost:

code 1

Here's the result after reinitialising the linked list, and running the second code; again, I created another reference to the Node(2) to prevent it from being lost:

code 2

We can see that the second code creates a cycle, so that the list doesn't have an end any more. That means any recursive algorithm which tries to iterate through the list will not terminate, except by overflowing the call stack.

Upvotes: 2

Karl Knechtel
Karl Knechtel

Reputation: 61508

Suppose head names element 0 of the list, which has a .next referring to element 1 etc. to start.

In the first case build the tuple (head, head.next.next), which is to say, (element 0, element 2). Then we assign those values to head.next.next and head.next respectively, and in that order. That is to say, the head.next (element 1 previously) has its next altered to refer to element 0, and the head (element 0) has its next altered to refer to element 2. We have reversed the element 0 -> element 1 link into an element 1 -> element 0 link, and by temporarily making element 0 connect to element 2, we can repeat the process.

In the second case, we simply have the operations swapped on both sides, so it would seem like the net effect should be the same. However, this time we are tripped up by a dependency. Because the assignment to head.next happens before the assignment to head.next.next, the head.next.next doesn't update the prior head.next, but the one we just set up. That is: element 0 is connected to element 2, but then element 2 (rather than element 1) is the head.next that gets connected to element 0. Now we have built a cycle between those two elements, and attempts to continue the procedure thus get stuck in a loop (if implemented iteratively, or in unbounded recursion as described by OP for a presumed recursive implementation).

Normal uses of this a, b = x, y syntax in Python involve a and b being unrelated. Here they are related, so the order of assignment matters, despite that the syntax is normally used specifically to avoid worrying about order of assignment (for "state updates"). Linked list logic is the sort of thing where order of assignment needs to be precise, which is another good reason not to implement this stuff yourself - except, of course, as an exercise to practice detailed and precise thinking about algorithms.

Upvotes: 1

Related Questions