9

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

sawa
  • 165,429
  • 45
  • 277
  • 381
nimeshkiranverma
  • 1,408
  • 6
  • 25
  • 48
  • This should be closed as a duplicate, not as opinion-based. The second sentence gives a clear criterium for what "preferred" in the first sentence means, IMO. I'm sure there's a duplicate somewhere, though. – Jörg W Mittag Jul 25 '14 at 10:54
  • This doesn't need to be closed at all. SO is not Wikipedia. We can have two or seven threads discussing closely related questions. With all due explicitly stated respect to your very high seniority status here. – Boris Stitnicky Jul 25 '14 at 11:31

5 Answers5

8

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.

Anil Purohit
  • 186
  • 4
  • Are you sure that those benchmarks are statistically sound? Since `for` desugars to `each`, I wouldn't expect there to be a difference. If at all, `for` should be very slightly faster since it doesn't need to allocate a new local scope because variables declared inside a `for` loop pollute the outer scope, whereas the block passed to `each` has its own scope. – Jörg W Mittag Jul 25 '14 at 10:52
  • @JörgWMittag, what? Does `for` really desugar to `each`? And, btw., I'd also expect `for` to be a bit faster for the same reason. I explain the observation of the opposite to Ruby devs paying more attention to optimizing `each` than `for`. – Boris Stitnicky Jul 25 '14 at 11:32
  • 1
    @BorisStitnicky: Yes, of course. There isn't much else it could desugar to, after all. See section 11.5.2.3.4 of the ISO Ruby Language Specification. `for var1, var2 in expression; do_something end` is evaluated as `expression.each do |var1, var2| do_something end` except that the block doesn't have its own scope. Try `def (a = Object.new).each; p 'each called'; yield 42 end; for e in a; p e end # 'each called' # 42 # => 42` – Jörg W Mittag Jul 25 '14 at 11:39
  • Thanks, it's good to know what precisely is happening behind the scenes. – Boris Stitnicky Jul 25 '14 at 11:55
4
  • 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.

Boris Stitnicky
  • 12,444
  • 5
  • 57
  • 74
sawa
  • 165,429
  • 45
  • 277
  • 381
3

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

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

Community
  • 1
  • 1
mvidaurre
  • 489
  • 3
  • 6
2

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"
Community
  • 1
  • 1
Boris Stitnicky
  • 12,444
  • 5
  • 57
  • 74
1

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.

Claudi
  • 5,224
  • 17
  • 30
  • 1
    Could you give an example of a complex loop where `for` is more suitable than `each`? – Stefan Jul 25 '14 at 07:06
  • Well, I was thinking that actually stepping can be done using the step() method and then calling each(). In such case `for` remains as an awful way to loop :D – Claudi Jul 25 '14 at 07:09
  • BTW, the equivalent `for` loop would be `for x in arr; puts x; end` – Stefan Jul 25 '14 at 07:19
  • I know, but I used that syntax for showing the drawbacks of using a for loop, specially if you come from the low level language world: using `for` the traditional way can make your code highly inefficient. – Claudi Jul 25 '14 at 07:23