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)?
TL;DR
for loop for performance in Ruby 1.8for loop to standards in existing projectseach loop to minimize side effectseach 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)
I have written a few plugins for SketchUp's Ruby API and I found that when iterating large collections (of geometry entities) I would get better performance with a for in loop over an each block.
I believe this to be because the for in loop doesn't create it's local scope and objects are reused instead of being created for every iteration as it would in the each loop.
EDIT: The speed gain depends on Ruby version. Using the test snippet used in this article http://blog.shingara.fr/each-vs-for.html:
Ruby 1.8.6:
user system total real
For 14.742000 0.000000 14.742000 ( 14.777000)
Each 18.190000 0.000000 18.190000 ( 18.194000)
Ruby 2.0.0
user system total real
For 5.975000 0.000000 5.975000 ( 5.990000)
Each 5.444000 0.000000 5.444000 ( 5.438000)
Things has greatly improved since the old 1.8.6. (Though, SketchUp extension developers still need to optimize against this version.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With