4

Intrigued by this question, I have played a bit with parallel assignment with arrays and method calls. So here's an paradigmatic example, trying to swap two members in an array, by their value:

deck = ['A', 'B', 'C']
#=> ["A", "B", "C"]
deck[deck.index("A")], deck[deck.index("B")] = deck[deck.index("B")], deck[deck.index("A")]
#=> ["B", "A"]
deck
#=> ["A", "B", "C"]

The array hasn't changed. But if we change the order of arguments, it works:

deck[deck.index("B")], deck[deck.index("A")] = deck[deck.index("A")], deck[deck.index("B")]
#=> ["A", "B"]
deck
#=> ["B", "A", "C"]

I guess it has to do with the order of calling the index methods within the assignment, but not see it clearly. Can someone please explain the order of things underneath, and why the first example doesn't swap the member, and second does?

5
  • Parallel assignment is useful, but for this sort of task where you're looking up the locations of the variables in the array before proceeding it seems like a Hash would be a more usable structure because of its direct lookup. If you're doing your assignment repeatedly, like in an inner loop, it will be costly and a hash will outrun it. Commented Nov 15, 2010 at 15:07
  • Agreed, the example here was hypothetical, taken partly from the other question. Commented Nov 15, 2010 at 15:55
  • And now I've created a question based on your question! Commented Dec 2, 2010 at 23:43
  • You should totally drop what you're doing and switch to functional programming! Commented Dec 2, 2010 at 23:43
  • @Greg: I reverted the title, I really don't think the question form is right. When naming my questions, I follow this simple principle: "How would I phrase the Google query I would like to match my SO question". Commented Dec 10, 2010 at 9:45

3 Answers 3

3

It is expected. It follows from how ruby evaluates expressions.

deck[deck.index("A")], deck[deck.index("B")] = deck[deck.index("B")], deck[deck.index("A")]

Implies

deck[deck.index("A")], deck[deck.index("B")] = 'B', 'A'

Note: strings 'A' and 'B' here are for illustration only. Ruby doesn't create new string objects here. Which essentially is:

deck[deck.index("A")] = 'B' -> deck[0] = 'B' (deck = ['B', 'B', 'C'])
deck[deck.index("B")] = 'A' -> deck[0] = 'A' (deck = ['A', 'B', 'C'])

Array#index returns when it finds the first match.

Now,

deck[deck.index("B")], deck[deck.index("A")] = deck[deck.index("A")], deck[deck.index("B")]
-> deck[deck.index("B")], deck[deck.index("A")] = 'A', 'B'
-> deck[deck.index("B")] = 'A' -> deck[1] = 'A' (deck = ['A', 'A', 'C'])
-> deck[deck.index("A")] = 'B' -> deck[0] = 'B' (deck = ['B', 'A', 'C'])
Sign up to request clarification or add additional context in comments.

Comments

1

Just as an example, compare the machinations used to search the array, find the correct indexes then swap the values, with what you could do using a Hash:

h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" }

h['dog'], h['cat'] = h.values_at('cat', 'dog')

h #=> {"cat"=>"canine", "dog"=>"feline", "cow"=>"bovine"}

Now, if Ruby had an assignable values_at= Hash method it could be even cleaner:

h.values_at('dog', 'cat') = h.values_at('cat', 'dog')

but, alas, we don't. Hash slicing is a very powerful tool in Perl and something I miss about Ruby.

And, yes, I know I can add my own assignable values_at=.

Comments

1

M Rajesh is correct, but he actually had to think in order to work it out. I'm too lazy for that!

Here's a printf-debugging way of showing what happened.

deck = ['A', 'B', 'C']
#=> ["A", "B", "C"]
deck[deck.index("A").tap {|index| 
  STDERR.puts "Result of indexing for #{"A".inspect} is #{index.inspect}"
  }], 
deck[deck.index("B").tap {|index| 
  STDERR.puts "Result of indexing for #{"B".inspect} is #{index.inspect}"
  }] = 
deck[deck.index("B")], deck[deck.index("A")]
# Result of indexing for "A" is 0
# Result of indexing for "B" is 0
#=> ["B", "A"]
deck
#=> ["A", "B", "C"]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.