Thursday, October 10, 2013

Ruby variable assignment... how does that really work?

I've recently been working to improve my Ruby skills, so I've started coding many of the exercises in "Cracking the Coding Interview" by Gayle Laakmann McDowell, implementing the problems in TDD/Ruby instead of in Java.

In the process, I ran into a real puzzler -- a bug that I and several friends looked at and couldn't quickly solve. It turned out to be really basic Ruby.

I had developed the following code as part of the answer to CtCI's problem 9.4: Create all the subsets from a set (you can find the complete solution here).
def ss_raw set
  @result_raw << set
  return if set.size == 1
  set.each_index do |i|
    tmp = set
    tmp.delete_at i
    ss_raw(tmp)
  end
end
The intent of this code was to recursively remove one element from the input and generate the subsets. The tmp variable was intended as a proxy for the set variable so that the each_index block could process each subset by deleting one element at a time. It didn't work and gave some really perplexing results, apparently replacing the contents of @result_raw rather than appending to it.

Unfortunately, I forgot the basics:
tmp = set
doesn't create a new variable, it simply creates a new pointer to the same location in storage that set points to. Therefore, modifying tmp also modifies set and the whole process goes south. The solution? Just say
tmp = set.clone
and you get a new and separate variable and the method works as expected.

In many cases, an oversight like this won't cause a problem, but in this cae it wound up modifying the method parameter as well as modifying the basis of the each_index block... a definite no-no and exactly what I was trying to avoid in the first place.

Hopefully this little reminder will help someone else avoid the pain.