Use of the ||= operator in loops and iterators

Something that’s come up a couple times while coding is how ||= seems to
behave differently between these two structures. Assume var is undefined
at the time of the loop/block. var ||= 1 ; var += 1 will keep it’s
value, incrementing with each loop. However, if var ||= 0; var += 1 will
end up always being one. Is this because each iterator is in it’s own
scope, while the loop is in the scope of the ‘larger environment’?
Therefore with each loop through the iterator the value is wiped out and
the ||= operator resets it to it’s value? I’m a beginner so I’m looking
for confirmation and perhaps more insight into scope. Thanks.

Here’s an example:

[1, 2].each do |i|
var ||= 0
var += 1
puts var
end

–output:–
1
1

  1. The block, i.e. the stuff after ‘do’, is like a def, e.g.:

def do_stuff(i)
var ||= 0
var += 1
puts var
end

  1. The block is passed to the method on the left of ‘do’, in this case
    Array#each().

  2. Inside the Array#each() method, the source code calls the block for
    each item in the Array passing the Array item as an argument, just
    like calling a def.

  3. After a block or def finishes executing, all the local variables
    created by the block or def are destroyed. As a result, each time the
    block is called, the var variable is created anew.

On the other hand, with a while loop, e.g.

i = 1

while i < 3
var ||= 0
var += 1
puts var

i += 1
end

–output:–
1
2

…the local variables created by the loop are NOT destroyed when a
single loop finishes. So the var variable is created the first time
through the loop, and then in subsequent loops the line var ||= 0
does nothing to var, and the next line increments the pre-existing
value of var.

Here is an example that makes it a little clearer how blocks are similar
to defs:

def my_each (&x)
x[1]
x[2]
x[3]
end

my_each do |num|
puts num*2
end

–output:–
2
4
6

The way that works is that the block specified in the call to my_each():

{|num| puts num*2}

is passed as an argument to the my_each() method. In the definition of
my_each(), the block is captured in a variable called x. Then, similar
to the way a def is called, the block is called and passed an argument.
To call blocks, you use brackets instead of parentheses. Typically, you
don’t see brackets used to call blocks very much because there is an
alternate way to call a block that is clearer:

def my_each (&x)
x.call(1)
x.call(2)
x.call(3)
end

So in ruby, you are always passing methods as arguments to other
methods. Ruby’s syntax allows you to call a pre-existing method and
immediately define an anonymous method that gets passed to the
pre-existing method.

There are differences between blocks and defs…but you will learn about
those differences in due time.

7stud already gives you a really good explanation, but I want to throw
in that if you define the variable outside the block, it will be
automatically available in the block’s scope (this is actually one
strength of blocks/closures).

var ||= 0
[1, 2].each { puts var += 1 }

This code will output:
1
2

Sometimes this behavior is unwanted, and with ruby >= 1.9, you can force
a variable’s name to be local to a block:

var = 1000
[1, 2].each do |; var| # the trick is the ; before var
puts “In block: #{var += 1}”
end
puts “Out block: #{var}”

This code will output:
In block: 1
In block: 1
Out block: 1000

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs