nimeshkiranverma
nimeshkiranverma

Reputation: 1428

What is prefered way to loop in Ruby?

Why is each loop preferred over for loop in Ruby? Is there a difference in time complexity or are they just syntactically different?

Upvotes: 9

Views: 1600

Answers (5)

Anil Purohit
Anil Purohit

Reputation: 186

Yes, these are two different ways of iterating over, But hope this calculation helps.

require 'benchmark'

a = Array( 1..100000000 )
sum = 0
Benchmark.realtime {
  a.each { |x| sum += x }
}

This takes 5.866932 sec

a = Array( 1..100000000 )
sum = 0
Benchmark.realtime {
  for x in a
    sum += x
  end
}

This takes 6.146521 sec.

Though its not a right way to do the benchmarking, there are some other constraints too. But on a single machine, each seems to be a bit faster than for.

Upvotes: 8

sawa
sawa

Reputation: 168101

  • The variable referencing an item in iteration is temporary and does not have significance outside of the iteration. It is better if it is hidden from outside of the iteration. With external iterators, such variable is located outside of the iteration block. In the following, e is useful only within do ... end, but is separated from the block, and written outside of it; it does not look easy to a programmer:

    for e in [:foo, :bar] do
      ...
    end
    

    With internal iterators, the block variable is defined right inside the block, where it is used. It is easier to read:

    [:foo, :bar].each do |e|
      ...
    end
    
  • This visibility issue is not just for a programmer. With respect to visibility in the sense of scope, the variable for an external iterator is accessible outside of the iteration:

    for e in [:foo] do; end
    e # => :foo
    

    whereas in internal iterator, a block variable is invisible from outside:

    [:foo].each do |e|; end
    e # => undefined local variable or method `e'
    

    The latter is better from the point of view of encapsulation.

  • When you want to nest the loops, the order of variables would be somewhat backwards with external iterators:

    for a in [[:foo, :bar]] do
      for e in a do
        ...
      end
    end
    

    but with internal iterators, the order is more straightforward:

    [[:foo, :bar]].each do |a|
      a.each do |e|
        ...
      end
    end
    
  • With external iterators, you can only use hard-coded Ruby syntax, and you also have to remember the matching between the keyword and the method that is internally called (for calls each), but for internal iterators, you can define your own, which gives flexibility.

Upvotes: 4

Boris Stitnicky
Boris Stitnicky

Reputation: 12578

An interesting question. There are several ways of looping in Ruby. I have noted that there is a design principle in Ruby, that when there are multiple ways of doing the same, there are usually subtle differences between them, and each case has its own unique use, its own problem that it solves. So in the end you end up needing to be able to write (and not just to read) all of them.

As for the question about for loop, this is similar to my earlier question whethe for loop is a trap.

Basically there are 2 main explicit ways of looping, one is by iterators (or, more generally, blocks), such as

[1, 2, 3].each { |e| puts e * 10 }
[1, 2, 3].map { |e| e * 10 )
# etc., see Array and Enumerable documentation for more iterator methods.

Connected to this way of iterating is the class Enumerator, which you should strive to understand.

The other way is Pascal-ish looping by while, until and for loops.

for y in [1, 2, 3]
  puts y
end

x = 0
while x < 3
  puts x; x += 1
end

# same for until loop

Like if and unless, while and until have their tail form, such as

a = 'alligator'
a.chop! until a.chars.last == 'g'
#=> 'allig'

The third very important way of looping is implicit looping, or looping by recursion. Ruby is extremely malleable, all classes are modifiable, hooks can be set up for various events, and this can be exploited to produce most unusual ways of looping. The possibilities are so endless that I don't even know where to start talking about them. Perhaps a good place is the blog by Yusuke Endoh, a well known artist working with Ruby code as his artistic material of choice.

To demonstrate what I mean, consider this loop

class Object
  def method_missing sym
    s = sym.to_s
    if s.chars.last == 'g' then s else eval s.chop end
  end
end

alligator
#=> "allig"

Upvotes: 2

Claudi
Claudi

Reputation: 5416

Aside of readability issues, the for loop iterates in the Ruby land whereas each does it from native code, so in principle each should be more efficient when iterating all elements in an array.

Loop with each:

arr.each {|x| puts x}

Loop with for:

for i in 0..arr.length
   puts arr[i]
end

In the each case we are just passing a code block to a method implemented in the machine's native code (fast code), whereas in the for case, all code must be interpreted and run taking into account all the complexity of the Ruby language.

However for is more flexible and lets you iterate in more complex ways than each does, for example, iterating with a given step.

EDIT

I didn't come across that you can step over a range by using the step() method before calling each(), so the flexibility I claimed for the for loop is actually unjustified.

Upvotes: 1

mvidaurre
mvidaurre

Reputation: 489

each is the Ruby Way. Implements the Iterator Pattern that has decoupling benefits.

Check also this: "for" vs "each" in Ruby

Upvotes: 3

Related Questions