views:

480

answers:

3

Suppose you do this in Ruby:

ar = [1, 2]
x, y = ar

Then, x == 1 and y == 2. Is there a method I can define in my own classes that will produce the same effect? e.g.

rb = AllYourCode.new
x, y = rb

So far, all I've been able to do with an assignment like this is to make x == rb and y = nil. Python has a feature like this:

>>> class Foo:
...     def __iter__(self):
...             return iter([1,2])
...
>>> x, y = Foo()
>>> x
1
>>> y
2
+1  A: 

You can't redefine assignment, because it's an operator instead of a method. But if your AllYourCode class were to inherit from Array, your example would work.

When Ruby encounters an assignment, it looks at the right hand side and if there is more than one rvalue, it collects them into an array. Then it looks at the left hand side. If there is one lvalue there, it is assigned the array.

def foo 
  return "a", "b", "c" # three rvalues
end

x = foo # => x == ["a", "b", "c"]

If there is more than one lvalue (more specifically, if it sees a comma), it assigns rvalues successively and discards the extra ones.

x, y, z = foo # => x == "a", y == "b", z == "c"
x, y = foo    # => x == "a", y == "b"
x, = foo      # => x == "a"

You can do parallel assignment if an array is returned, too, as you have discovered.

def bar
  ["a", "b", "c"]
end

x = bar       # => x == ["a", "b", "c"]
x, y, z = bar # => x == "a", y == "b", z == "c"
x, y = bar    # => x == "a", y == "b"
x, = bar      # => x == "a"

So in your example, if rb is an Array or inherits from Array, x and y will be assigned its first 2 values.

Sarah Mei
I think you meant to put brackets around "a", "b", "c" in your first example. When I tried it, I got a syntax error.
allyourcode
Actually I forgot the "return". Fixed now.
Sarah Mei
+2  A: 

Almost:

class AllYourCode
   def to_a
     [1,2]
   end
end

rb = AllYourCode.new
x, y = *rb
p x
p y

Splat will try to invoke to_ary, and then try to invoke to_a. I'm not sure why you want to do this though, this is really a syntactical feature that happens to use Array in its implementation, rather than a feature of Array.

In other words the use cases for multiple assignment are things like:

# swap
x, y = y, x

# multiple return values
quot, rem = a.divmod(b)

# etc.
name, age = "Person", 100

In other words, most of the time the object being assigned from (the Array) isn't even apparent.

Logan Capaldo
This is somewhat helpful, but I hate it when I ask a question and people believe I have no good reason to do what I'm trying to do. If you think this is a bad idea, explain why!! I appreciate your examples, but showing typical uses doesn't explain what's wrong with what I'm trying to do :/
allyourcode
I didn't say you didn't have a good reason, I just said I don't know what your reason is. I can't voice an opinion on whether it's good or not until I know what it is. I certainly can't say there is never a circumstance where you'd want to do this, but if you'd indulge my curiosity I'd appreciate it.
Logan Capaldo
+4  A: 

Yep. Define #to_ary. This will let your object be treated as an array for assignment.

irb> o = Object.new
=> #<Object:0x3556ec>
irb> def o.to_ary
       [1, 2]
     end
=> nil
irb> x, y = o
=> [1,2]
irb> x
#=> 1
irb> y
#=> 2

The difference between #to_a and #to_ary is that #to_a is used to try to convert a given object to an array, while #to_ary is available if we can treat the given object as an array. It's a subtle difference.

rampion
Subtle indeed; this is similar distinction between == and eql? which I really don't like. Why is a good idea to have to_a AND to_ary ???
allyourcode
to_ary is for objects that are effectively arrays. If something that really needs to use an array can use your object instead of an array and your object is meant to be an array, then it should implement to_ary. to_a is for things that are convertible to arrays, e.g. objects with (terminating) each methods have an obvious implementation of to_a. E.g. a file could have a to_a method, but you can't treat it as an array, and it doesn't model an array so it wouldn't have a to_ary method.
Logan Capaldo