The behavior of iterator methods

Hi all,

Sorry, I deleted my previous post on the ruby forum by mistake.
So I post it again.

I am a Ruby-beginner.
I have some experience of C/C++ for several years.
It has been only one week since I started to learn Ruby.
Please tell me about the behavior of iterator methods.

At first, I did as bellow on the irb environment.

irb(main):001:0> s = [“a”, “b”, “c”]
=> [“a”, “b”, “c”]
irb(main):002:0> s[0].object_id
=> 36971304
irb(main):003:0> s[1].object_id
=> 36971292
irb(main):004:0> s[2].object_id
=> 36971280
irb(main):005:0> s.each{|c| c.upcase!}
=> [“A”, “B”, “C”]
irb(main):006:0> p s
[“A”, “B”, “C”]
=> nil

Looking at this, I thought I can change the value of each element in
Array object through iterator methods.

However, when I did next as bellow, that behavior looked different.

irb(main):007:0> a = [1,2,3]
=> [1, 2, 3]
irb(main):008:0> a[0].object_id
=> 3
irb(main):009:0> a[1].object_id
=> 5
irb(main):010:0> a[2].object_id
=> 7
irb(main):011:0> a.each{|i| i += 1}
=> [1, 2, 3]
irb(main):012:0> p a
[1, 2, 3]
=> nil

irb(main):013:0> a.each{|i| p i.object_id}
3
5
7
=> [1, 2, 3]

I expected that Array#each method with the code block would change
contents of the Array object variable named ‘a’ into [2,3,4].
But it didn’t. Why?

The object IDs of a[0], a[1], a[2] are shown through
the the code block of Array#each. So I thought if variable named ‘i’
changed its value, that had to be reflected to the Array object ‘a’.
But it didn’t.

Why did this difference happened?

Some methods change objects in place, others return a changed object and
leave the original in place.

The bang is placed by convention at the end of methods that have some
unusual behavior; most often, this unusual behavior is that a data
structure is passed in by reference not by value.

i is a local variable to the block. each() assigns each value in the
array to the variable i. Then you add 1 to i, then the block ends and
the variable i is destroyed. That has no effect on the original array.

That’s where I misunderstood.
I don’t know why, but I thought the ‘i’ vareable like a pointer of a
pointer in C/C++. Maybe I was confused.

Now I can understand what was wrong.
Thank you very much.

Well, let’s take a look at the docs:

http://www.ruby-doc.org/core-1.9.3/Array.html

Hmm…the typically crummy ruby docs are no help: each() returns ‘ary’,
which is the arity of the each() method. Wait, or does ‘ary’ stand for
array? If it stands for array–then what array?
Some new array? The original array?? Hmm…very confusing. each()
seems to be the most popular method in ruby, and that is the best the
docs can do?

We’ll have to figure things out for ourselves:

x = [1, 2, 3]
puts x.object_id

result = x.each {|num| num += 1}
p result
puts result.object_id

–output:–
2152279020
[1, 2, 3]
2152279020

Ah hah. So ‘ary’ means ‘array’, and each() just returns the original
array. Wow, nice docs. If that doesn’t have you running straight for a
beginning python book…

So I thought if variable named ‘i’
changed its value, that had to be reflected to the Array
object ‘a’. But it didn’t.

i is a local variable to the block. each() assigns each value in the
array to the variable i. Then you add 1 to i, then the block ends and
the variable i is destroyed. That has no effect on the original array.
Here is a simpler example of how that works:

x = [1, 2, 3]

z = x[0]
z += 10
p x

–output:–
[1, 2, 3]

Ruby numbers are immutable so when you write z += 10, that is equivalent
to z = z + 10, and ruby takes the value of z, which is 1, and the value
10 and creates a new Integer object whose value is 11, then the new
Integer object gets assigned to z. That doesn’t affect the Integer
object, i.e 1, that x[0] refers to.

To affect the original array, you would have to do this:

x = [1, 2, 3]

x.length.times {|i| x[i] += 1}
p x

–output:–
[2, 3, 4]

I would look into Blocks, Procs, and Lambdas. The bit in the {} (or can
also be do end) is what’s called a block. Procs and Lambdas are
basically storing Blocks for reuse. The |i| is the iterator variable.
Make sure to pay close attention to yields, injects, and other such
methods in both Blocks and Enumerables, it’ll save you a load of time.

(For instance: (1…5).inject(:*) will return 120, or 5! (factorial))

http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/

Thank you for the explanation.
I appreciate any helps.

(1…5).inject(:*) will return 120, or 5!

this is far beyond my reach at the moment, hahaha…
Ruby clearly have some unique features I haven’t know yet.

By the way, the first time I had look at a ruby’s syntax and
some progurams using Procs or lambdas(maybe), it reminded me
Curl language, which I used to use for a few years.
http://www.curl.com/products/prod/language/

Do you know it?
I think Curl isn’t and won’t be famous as Ruby,
but it might be interesting for some Ruby programmers.

ten ten wrote in post #1076743:

irb(main):001:0> s = [“a”, “b”, “c”]
=> [“a”, “b”, “c”]
irb(main):002:0> s[0].object_id
=> 36971304
irb(main):003:0> s[1].object_id
=> 36971292
irb(main):004:0> s[2].object_id
=> 36971280
irb(main):005:0> s.each{|c| c.upcase!}
=> [“A”, “B”, “C”]
irb(main):006:0> p s
[“A”, “B”, “C”]
=> nil

It may be instructive to compare:

s.each{|c| c << “X”}
s.each{|c| c += “X”}

The first mutates the object by adding a character to it. The second is
short for c = c + “X”; it creates a new object from concatenating the
two, and assigns the new object to local variable c. However it does not
change the object which is still referenced from the array.

On Wed, Sep 19, 2012 at 10:43 PM, Jam B. [email protected] wrote:

At first, I did as bellow on the irb environment.
=> [“A”, “B”, “C”]
irb(main):007:0> a = [1,2,3]
[1, 2, 3]
contents of the Array object variable named ‘a’ into [2,3,4].

Posted via http://www.ruby-forum.com/.

You may also want to look into the map method for similar functionality.

String arrays are more easily constructed using %w (for single quote)
and
%W (for double quote) like so:

a = %w(a b c)
b = a.map!{ |word| word.upcase }
puts b

Returns:

A

B

C

http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-map

This is what you’re probably looking for. Make sure to read that page as
well as the one on enumerators, quite a bit of sugar and goodies to be
had
to those who read deep into those docs.

Depending on your paradigm/style though, some would say that bang
methods
should not be used to preserve the purity of a variable. To be fair,
that’s
a functional mindset of stateless variables and immutability, and seeing
how you come from a C type background I doubt you code that way. Ruby is
a
hybrid that allows you to engage in some functional behavior, but a good
bit of it is encouraged by the style, such as Blocks.

I would look into Blocks, Procs, and Lambdas. The bit in the {} (or can
also be do end) is what’s called a block. Procs and Lambdas are
basically
storing Blocks for reuse. The |i| is the iterator variable. Make sure to
pay close attention to yields, injects, and other such methods in both
Blocks and Enumerables, it’ll save you a load of time.

(For instance: (1…5).inject(:*) will return 120, or 5! (factorial))

http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/

This should get you well on your way in that regard.

Feel free to ask if you’re having any more trouble, always glad to help.

Brandon W.

So you can accomplish what you want like so.

a = [1, 2, 3]
a.each_with_index { |x, index| a[index] += 1 }
puts a

Or

(0…a.length).each { |x| a[x] += 1 }

On 09/21/2012 04:32 PM, Cliff R. wrote:

So you can accomplish what you want like so.

a = [1, 2, 3]
a.each_with_index { |x, index| a[index] += 1 }
puts a

Or

(0…a.length).each { |x| a[x] += 1 }

Simpler:

a.map! {|x| x + 1}

Simplest:

a.map! &:succ

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