Поскольку один из элементов, которые вы изменяете, является атрибутом другого, они не зависят друг от друга - необходим порядок сериализации, чтобы определить, что будет делать операция, и эта операция слева направо.
Давайте посмотрим, как это работает, написав этот код, как это было бы с временными переменными.
Учитывая следующую общую прелюдию:
old_nodeA = nodeA
old_nodeB = nodeB
old_nodeA_next = nodeA.next
Рабочий код похож на следующий:
# nodeB, nodeA.next, nodeA = nodeA, nodeB, nodeA.next
nodeB = old_nodeA
nodeA.next = old_nodeB # nodeA is still the same as old_nodeA here
nodeA = old_nodeA_next
Вот сломанный код:
# nodeB, nodeA, nodeA.next = nodeA, nodeA.next, nodeB
nodeB = old_nodeA
nodeA = old_nodeA_next
nodeA.next = old_nodeB # we're changing old_nodeA_next.next, not old_nodeA.next
Разница в том, что nodeA.next
относится к атрибуту next
разного nodeA
между двумя случаями.
Давайте посмотрим, как это работает во время выполнения в случае, когда все работает правильно, с некоторым псевдокодом, показывающим идентификаторы объектов, чтобы вы могли различатьмежду объектами, которые были видоизменены на месте по сравнению с измененными ссылками:
# Working implementation
###############################################################
# id(nodeA) # id(nodeB) # AAA.v # AAA.next # BBB.v # BBB.next #
###############################################################
# AAA # BBB # 1 # BBB # 2 # None # Starting condition
# AAA # AAA # 1 # BBB # 2 # None # nodeB = old_nodeA
# AAA # AAA # 1 # BBB # 2 # None # nodeA.next = old_nodeB
# BBB # AAA # 1 # BBB # 2 # None # nodeA = old_nodeA_next
В рабочем сценарии мы переключили имена A
и B
, чтобы каждый ссылался на противоположный узел;ничего не изменилось.
Для сравнения:
# Broken implementation
###############################################################
# id(nodeA) # id(nodeB) # AAA.v # AAA.next # BBB.v # BBB.next #
###############################################################
# AAA # BBB # 1 # BBB # 2 # None # Starting condition
# AAA # AAA # 1 # BBB # 2 # None # nodeB = old_nodeA
# BBB # AAA # 1 # BBB # 2 # None # nodeA = old_nodeA_next
# BBB # AAA # 1 # BBB # 2 # BBB # nodeA.next = old_nodeB
Когда мы добрались до nodeA.next = old_nodeB
, имени nodeA
уже был присвоен идентификатор, изначально связанный с узлом B (BBB
в нашем примере), поэтому мы изменили оригинальный узел B указатель next
, чтобы он указывал на себя, создавая цикл в основе проблемы.