I was thinking about the use of for loops in Ruby the other day, triggered by one of the Ruby problems over at rubeque.
Now, a for-loop in Ruby usually looks something like this:
for i in collection # i takes each value that's in the collection end
Interesting things happen when the loop variable (i in the case above) is assigned to a value already before the loop. In particular, interesting things can happen if it’s not even a variable.
Let’s see what one can do with a for-loop in Ruby (or rather to a for-loop). Enter pry (or your favourite Ruby-REPL [Read-Evaluate-Print-Loop]):
pry(main)> for Object.new in [1,2,3]; end
NoMethodError: undefined method `new=’ for Object:Class
from (pry):3:in `block in __pry__’
Oh, that’s interesting: The interpreter does not complain about Object.new not being a variable, but rather that there’s not method ‘new=’ for class Object.
OK. let’s play with this… Let’s assume a class Foo:
class Foo def initialize foo @foo = foo end def foo= other @foo.unshift other end end
Note that there’s no method named foo for class Foo, just ‘foo=’. Initialising a variable now with, say, an empty Array, there’s a curious way to reverse an Array:
pry(main)> f = Foo.new(res) => #<foo:0x007fd392c02368 @foo=[]> pry(main)> pry(main)> for f.foo in [:first, 2, 'third'] pry(main)* end => [:first, 2, "third"] pry(main)> pp res ["third", 2, :first] => ["third", 2, :first]
There is a way to use an empty loop to reverse an Array. (Note. this does not solve the problem given at rubeque, since a class definition is not allowed inside a method definition). In fact there are more ways than this!
A few new things I learned about for loops in Ruby:
- They do not require a variable name as the ‘loop variable’
- If one uses what looks like a method call (the ‘f.foo’ in the example above) as the ‘loop variable’ then…
- …the object f refers to, doesn’t even need to respond to that method, but
- …instead it needs to respond to ‘method_name=’ (‘foo=’ in the example above).
I don’t know what to think about this, but you probably shouldn’t rely on this behaviour or use it in production code.