Is there any case to prefer for instead of each in Ruby?

ruby

Solution

TL;DR

- Use `for` loop for performance in Ruby 1.8

- Use `for` loop to standards in existing projects

- Use `each` loop to minimize side effects

- Prefer `each` loop.

`each` minimizes side effects

The primary difference between `for` and `each` is scoping.

The `each` function takes a block. Blocks create a new lexical scope. This means that any variables declared within the scope of the function will no longer be available after the function.

[1, 2, 3].each do |i|
  a = i
end

puts a
# => NameErrror: undefined local variable or method `a' for main:Object

Whereas:

for i in [1, 2, 3]
  a = i
end

puts a
# => 3

Thus, using the `each` syntax minimizes the risk of side effects.

Determing exit point?

This said, there are special instances where the `for` loop may be helpful. Specifically, when finding out where a loop exited.

for i in 1..3
  a = i

  break if i % 2 == 0
end

puts a
# => 0

There is a better way to do this though:

a = (1..3).each do |i|
  break i if i % 2 == 0
end

Each is faster (in Ruby 2.0)

Benchmark.bm(8) do |x|
  x.report "For" do
    max.times do
      for i in 1..100
        1 + 1
      end
    end
  end

  x.report "Each" do
    max.times do
      (1..100).each do |t|
        1+1
      end
    end
  end
end

Ruby 2.0

               user     system      total        real
For        6.420000   0.000000   6.420000 (  6.419870)
Each       5.830000   0.000000   5.830000 (  5.829911)

Ruby 1.8.6 (Slower Machine)

              user     system      total        real
For      17.360000   0.000000  17.360000 ( 17.409992)
Each     21.130000   0.000000  21.130000 ( 21.250754)

Benchmarks 2

If you read the comment trail, there is a discussion about the speed of creating objects in `for` vs `each`. The link provided had the following benchmarks (although, I have cleaned up the formatting and fixed the syntax errors).

b = 1..10e5

Benchmark.bmbm (10) do |x|
  x.report "each {}" do
    b.each { |r| r + 1 }
  end

  x.report "each do end" do
    b.each do |r|
      r + 1
    end
  end

  x.report "for do end" do
    for r in b do
      r + 1
    end
  end
end

Ruby 2.0

                  user     system      total        real
each {}       0.150000   0.000000   0.150000 (  0.144643)
each do end   0.140000   0.000000   0.140000 (  0.143244)
for do end    0.150000   0.000000   0.150000 (  0.147112)

Ruby 1.8.6

                  user     system      total        real
each {}       0.840000   0.000000   0.840000 (  0.851634)
each do end   0.730000   0.000000   0.730000 (  0.732737)
for do end    0.650000   0.000000   0.650000 (  0.647186)

Problem

I know that `for i in arr` slightly differs from `arr.each` scope and everyone keeps saying that iterators are preferrable, but I wonder if there is any case when cycle is preferrable and why it is (since iterators are more idiomatic)?

Original source

Related problems