Параллельное присвоение в ruby, работающее по-разному для двух эквивалентных фрагментов кода

#ruby #arrays #slice #parallel-assignment

#ruby #массивы #фрагмент #параллельное присвоение

Вопрос:

Два приведенных ниже фрагмента кода должны печатать одно и то же, но они этого не делают.

 ary = %W(1 2 5 6 B 8 5 4 6 5 6 9 7 A)
indx1 = 0...ary.index("B")
indx2 = (ary.index("A")   1)..-1
ary[indx1], ary[indx2] = ary[indx2], ary[indx1]
puts ary.inspect
  

 ary = %W(1 2 5 6 B 8 5 4 6 5 6 9 7 A)
ary[0...ary.index("B")], ary[(ary.index("A")   1)..-1] = ary[(ary.index("A")   1)..-1],  ary[0...ary.index("B")]
puts ary.inspect
  

Первый выводит:

 ["B", "8", "5", "4", "6", "5", "6", "9", "7", "A", nil, nil, nil, nil, "1", "2", "5", "6"]
  

и второй:

 ["B", "8", "5", "4", "6", "5", "6", "9", "7", "A", "1", "2", "5", "6"]
  

Разве они не должны печатать одно и то же? Мне они кажутся эквивалентными.

(С использованием Mac OSX 10.6.7 и ruby 1.9.2-p180)

Ответ №1:

Они отличаются тем, что в первом случае вы предварительно вычисляете индексы, а во втором вы вычисляете их динамически, и массив изменяется между первым присвоением и вторым.

Технически обе оценки RHS выполняются до выполнения назначений LHS, но оба назначения LHS не могут быть выполнены одновременно, поэтому в данном случае вы фактически видите, что

 A, B = C, D
  

эквивалентно

 A = C
B = D
  

Итак, первое, что вы делаете, это…

 ary[0...ary.index("B")] = ary[(ary.index("A")   1)..-1]
  

в обоих случаях. Итак, ary теперь

 ["B", "8", "5", "4", "6", "5", "6", "9", "7", "A"]
  

Теперь изначально вы рассчитали indx1 и indx2 как 0...4 и 14..-1 соответственно, но теперь, если вы пересчитали значения indx1 и indx2, вы получите:

 indx1 = 0...ary.index("B")       #=> 0...0
indx2 = (ary.index("A")   1)..-1 #= 10..-1
  

Другими словами,

 ary[indx2] = ary[indx1]
  

больше не эквивалентно

 ary[(ary.index("A")   1)..-1] = ary[0...ary.index("B")]
  

Это,

 ary = ["B", "8", "5", "4", "6", "5", "6", "9", "7", "A"]
ary[(ary.index("A")   1)..-1] = ary[0...ary.index("B")]
  

дает вам

 ary #=> ["B", "8", "5", "4", "6", "5", "6", "9", "7", "A"]
  

в то время как

 ary = ["B", "8", "5", "4", "6", "5", "6", "9", "7", "A"]
ary[indx2] = ary[indx1]
  

дает вам

 ary #=> ["B", "8", "5", "4", "6", "5", "6", "9", "7", "A", nil, nil, nil, nil, "1", "2", "5", "6"]