Volatile variables in loops?

irb(main):009:0* 5.times{|i; j| if i == 0 then j = 1 end; print i, j,
“\n”}
01
1
2
3
4
=> 5

I read about the behaviour of blocks and variables in “The Ruby
Programming Language” but can not really remember this topic for loops
– it really surprises me. (I found a similar question at
stackoverflow.com, but no good answer.)

So Ruby 1.9.3 forgets all local variable values for each loop iteration.
Good to know this. Was it mentioned clearly in the documentation or the
textbooks? And for what is this behaviour in loops really good? I am
really happy that it took me only 10 minutes to find that problem in my
code, I should try to keep it in my head. ( Of course the solution, as
mentioned in Matz’s book, is simple a j=0 in front of the loop.)

Best regards

Stefan S.

On 11/05/2013 03:42 PM, Stefan S. wrote:

Programming Language" but can not really remember this topic for loops
Best regards

Stefan S.

This is a block, just like any other block. The code is explicitly
setting j to be a block local variable, so each time the block is
invoked it is a fresh variable. There is no difference between your code
and this code, assuming j is not an existing variable:

5.times{|i| j = nil; if i == 0 then j = 1 end; print i, j, “\n”}

(Actually, you don’t even need j = nil, but that’s more of an accident
of Ruby’s implementation.)

Just like one wouldn’t expect a variable local to a method to persist
each time the method is called, neither does a variable local to a
block. The difference is that a block is also a closure, so it can
reference and manipulate variables in the scope surrounding it.

-Justin

On Tue, 2013-11-05 at 16:23 -0800, Justin C. wrote:

This is a block, just like any other block.

Yes indeed – in Ruby each loop is in first order a block, that is what
I forgot. For a block this behaviour is well documented. I have a loop
with two iterations, in the first iteration I make some expensive
calculations, which I would like to reuse in the second iteration. So I
have to define that variables before the loop, and of course I have to
write j = 0; 2.times{|i| – not 2.times{|i; j|.

I should really try hard to remember that.

Thanks,

Stefan S.

On Wed, Nov 6, 2013 at 1:40 AM, Stefan S. [email protected]
wrote:

On Tue, 2013-11-05 at 16:23 -0800, Justin C. wrote:

This is a block, just like any other block.

Yes indeed – in Ruby each loop is in first order a block,

Not each loop but #each loop. :slight_smile:

$ ruby -e ‘for i in 0…5; j=i; puts i; end; puts “–”, j’
0
1
2
3
4
5

5

There is no block with a for loop.

that is what
I forgot. For a block this behaviour is well documented. I have a loop
with two iterations, in the first iteration I make some expensive
calculations, which I would like to reuse in the second iteration. So I
have to define that variables before the loop, and of course I have to
write j = 0; 2.times{|i| – not 2.times{|i; j|.

I should really try hard to remember that.

Basically it’s the same with most languages with nested scopes:
variables are visible in the smallest surrounding scope. There are
some subtleties in some languages but that rule covers many cases -
certainly for the more popular languages of today.

Depending on what you do #map or #inject might be alternatives (i.e.
if you calculate values per original value or aggregate data in a
single object). There’s also #each_with_object.

Kind regards

robert

On Wed, 2013-11-06 at 08:34 +0100, Robert K. wrote:

0
1
2
3
4
5

5

There is no block with a for loop.

Thanks for that important hint.
One point, which has confused me, is that blocks can be limited by {} or
a do/end pair. But for the for loop (also while and until loops I think)
the do/end keywords do not build a block.

Best regards

Stefan S.

On Wed, Nov 6, 2013 at 3:37 PM, Stefan S. [email protected]
wrote:

$ ruby -e ‘for i in 0…5; j=i; puts i; end; puts “–”, j’

Thanks for that important hint.
One point, which has confused me, is that blocks can be limited by {} or
a do/end pair. But for the for loop (also while and until loops I think)
the do/end keywords do not build a block.

Probably because there are no “do/end” keywords.

robert

@Stefan

It’s nothing like that Ruby is forgetting the loop’s local variables or
so…

To me, the code has some problems. We are actually initializing the “j”
in
every repetition of the loop and that’s why it is not printing anything
for
a nil object.
What we are doing above is exactly like this:

Ruby 1.9.3

a = 1
print a # => prints 1 and returns nil
a = nil
print a # => prints blank and returns nil

Ruby 1.8.7

a = 1
print a # => prints 1 and returns nil
a = nil
print a # => prints “nil” and returns nil

So, only difference in Ruby 1.8 and 1.9.3 is that earlier it used to
print
“nil” for nil objects and now it doesn’t print anything for nil objects
which logically sounds better.

In your code if you’d remove the initialization of “j” in every rep of
the
loop you’ll get what you want:

e.g.

Ruby 1.9.3

5.times{|i| if i == 0 then j = 1 end; print i, j,“\n”}
01
11
21
31
41
=> 5

Note that the block variables now is only |i| and not |i; j|

regards,
Sur
crismon9.com

On Thu, 2013-11-07 at 07:28 +0530, Sur wrote:

=> 5

Note that the block variables now is only |i| and not |i; j|

$ ruby -v
ruby 1.9.3p448 (2013-06-27 revision 41675) [x86_64-linux]
stefan@AMD64X2 ~ $ irb
irb(main):001:0> 5.times{|i| if i == 0 then j = 1 end; print i, j,"\n"}
01
1
2
3
4
=> 5

I will get your result when j is declared prior outside of the
block/loop – I was aware of that.

irb(main):003:0> j = nil; 5.times{|i| if i == 0 then j = 1 end; print i,
j,"\n"}
01
11
21
31
41
=> 5

I have to remember that .each and .times use blocks, so I have to
consider block scoping rules. But for, while and until loops do not need
blocks. Thanks, I think I understand this now.

On 11/06/2013 05:58 PM, Sur wrote:

Ruby 1.9.3
a = nil

/Note that the block variables now is only |i| and not |i; j|/

regards,
Sur
crismon9.com http://crismon9.com

This is not accurate.

1.8.7 :001 > 5.times{|i| if i == 0 then j = 1 end; print i, j,“\n”}
01
1nil
2nil
3nil
4nil
=> 5

1.9.3p448 :001 > 5.times{|i| if i == 0 then j = 1 end; print i, j,“\n”}
01
1
2
3
4
=> 5

-Justin