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