34
loading...
This website collects cookies to deliver better user experience
for
loops the concept of each
, anonymous functions, blocks, and all of those new terms feels very foreign.for
loops? Well we're going to cover that one today.for
loop:for item in [1, 2, 3]
puts item + 1
end
# 2
# 3
# 4
each
far more frequently.each
in Ruby is the de facto way of iterating through a collection:[1, 2, 3].each do |item|
puts item + 1
end
# 2
# 3
# 4
do ... end
starts what we call a block function, or anonymous function in other languages, and |item|
represents the arguments to that function. For each iteration of the loop each item will be fed into that function as an argument.each
, and we'll mention that in a bit, but first let's cover a few concerns about for
in Ruby.for
loops in Ruby, and we'll quickly cover a few of them.for
loop in Ruby is using each
behind the scenes, so even if you're not using it you're still using it. That also means that it's slower:require 'benchmark/ips'
# => true
collection = (1..100).to_a
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, ...
Benchmark.ips do |bench|
bench.report("for loop") do
sum = 0
for item in collection
sum += item
end
sum
end
bench.report("each loop") do
sum = 0
collection.each do |item|
sum += item
end
sum
end
end
# Warming up --------------------------------------
# for loop 22.017k i/100ms
# each loop 23.543k i/100ms
# Calculating -------------------------------------
# for loop 218.466k (± 2.6%) i/s - 1.101M in 5.042495s
# each loop 231.274k (± 2.1%) i/s - 1.177M in 5.092110s
for
loops leak variables into their outer scope:for item in collection
sum ||= 0
sum += item
end
item
# => 100
sum
# => 5050
item
it'll be overwritten. Same with sum
. Contrast with each
here:collection.each do |item2|
sum2 ||= 0
sum2 += item2
end
item2
# => nil
sum2
# NameError (undefined local variable or method `sum2' for main:Object)
each
, and related methods in Ruby rather than a for
loop? This section will look into that.sum2
being undefined here. Believe it or not that's quite useful later on, but has been known as a stumbling block to some.sum = 0
[1, 2, 3].each do |item|
sum += item
end
sum
# => 6
sum
as it's in the context of the block function, or what's immediately around it when it runs. This can be really useful for more advanced code, as that means functions effectively have memory, and in Ruby you can even redefine where it finds its memory by changing its context, but that's considerably more advanced.item
as it's only visible inside the block function. This can present some headaches, and early on in my Ruby career this confused me to no end:require 'net/ssh'
# Don't actually use passwords if you do this, use keys
Net::SSH.start('hostname', 'username', password: 'password') do |ssh|
config = ssh.exec! "cat /tmp/running.cfg"
end
defined?(config)
# => nil
config = nil
# Don't actually use passwords if you do this, use keys
Net::SSH.start('hostname', 'username', password: 'password') do |ssh|
config = ssh.exec! "cat /tmp/running.cfg"
end
defined?(config)
# => local-variable
Net::SSH
docs you might find that the block isn't even entirely necessary for this and get around the issue entirely. Anyways, point being there are some traps there potentially for the unaware, so be careful on what isolated block function scopes mean.Enumerable
which is one of the most powerful features of the language.sum = 0
for item in 1..100
sum += item * 2 if item > 4 && item.even?
end
sum
# => 5088
Enumerable
we can express each one of those conditions as a distinct transformation or filtering of the list:(1..100).select { |v| v.even? && v > 4 }.map { |v| v * 2 }.sum
# => 5088
words = %w(the rain in spain stays mainly on the plane)
words.map { |w| w[0] }.tally
# => {"t"=>2, "r"=>1, "i"=>1, "s"=>2, "m"=>1, "o"=>1, "p"=>1}
words.group_by { |w| w.size }
# => {3=>["the", "the"], 4=>["rain"], 2=>["in", "on"], 5=>["spain", "stays", "plane"], 6=>["mainly"]}
for
loops rather than iterables or enumerable, depending on the way you describe them. That said, most all languages including Java have a Streaming type library which does something very close to this.