Arrays

I am trying to create an array of certain length, with a default value of
Array.new (such that I will have an array of arrays). I cannot use the
"default" parameter setting Array.new( 4, Array.new ) because I end up with
an array populated with references to the same array. For example:

> a = Array.new( 4, Array.new )
> [ [], [], [], [] ]
> a[0][0] = 1
> a
> [ [1], [1], [1], [1] ]

After some thought, this result was not surprising. The interpreter
evaluates the Array.new parameter before passing, thus it ends up passing a
reference, and this single reference gets populated into each element,
instead of a new array into each.

My next approach was to create the array, with a dafault length, and iterate
over it assigning a new array to each element:

my_arr = Array.new( 4 )
my_arr.each{ |i|
    i = Array.new
}

my_arr.each { |j|
    puts j.type
}

I get the following output:
NilClass
NilClass
NilClass
NilClass
[nil, nil, nil, nil]

I was finally able to achieve the desired result by using array.each_index:

my_arr.each_index{ |m|
    my_arr[m] = Array.new
}

my_arr.each{ |n|
    puts n.type
}

Output:
Array
Array
Array
Array
[ [], [], [], [] ]

Conclusion: I understand why I can’t pass Array.new as the default value in
Array.new. However, I do not understand why the first iterative approach
does not work. I obviously know how to acquire the desired result, but that
doesn’t quench my curiousity as to why the other does not work. Can I
bother someone for some enlightenment?

Matt

···

Protect your PC - get McAfee.com VirusScan Online
http://clinic.mcafee.com/clinic/ibuy/campaign.asp?cid=3963

[snip]

In 1.8, you can use (pasted from ri 1.8):

 Array.new( size ) {| i | block } -> anArray

 In [this] form, an array of the given size is created. Each element in

this array is calculated by passing the element’s index to the given block and
storing the return value.

    squares = Array.new(5) {|i| i*i}
    squares             #=> [0, 1, 4, 9, 16]

So, for your purposes:

irb(main):001:0> a = Array.new(4) { |i| }
[, , , ]
irb(main):002:0> a[0].push(“foo”)
[“foo”]
irb(main):003:0> a[2].push(“bar”)
[“bar”]
irb(main):004:0> a
[[“foo”], , [“bar”], ]

I hope that helps, it helped me when Guy pointed out the same feature for
Hashes.

---------------------------------------------- | --------------------------
Brett Williams | (970) 288-0475

Agilent Technologies brett_williams@agilent.com
···

On May 1, Orion Hunter wrote:

I am trying to create an array of certain length, with a default value of
Array.new (such that I will have an array of arrays). I cannot use the
“default” parameter setting Array.new( 4, Array.new ) because I end up with
an array populated with references to the same array. For example:

a = Array.new( 4, Array.new )
[ , , , ]
a[0][0] = 1
a
[ [1], [1], [1], [1] ]

After some thought, this result was not surprising. The interpreter
evaluates the Array.new parameter before passing, thus it ends up passing a
reference, and this single reference gets populated into each element,
instead of a new array into each.

a = Array.new( 4, Array.new )

Well, with 1.8 you'll able to write

pigeon% ruby -e 'a = Array.new(4) { }; a[0][0] = 1; p a'
[[1], , , ]
pigeon%

My next approach was to create the array, with a dafault length, and iterate
over it assigning a new array to each element:

there is a problem : you assign a new array to the variable `i' and not to
each element

    my_arr = Array.new( 4 )
    my_arr.each{ |i|

at this step the variable `i' reference an element of the array

        i = Array.new

but here `i' will make reference to a new Array

    }

This is like if you write :

    i = my_arr[0]
    i =

for the first line , `i' make reference to the object `nil' (my_arr[0])
but in the second line, it will make reference to the object `' and
my_arr[0] is not modified

but you can write

pigeon% cat b.rb
##!/usr/bin/ruby
my_arr = (1..4).collect { }
my_arr.each {|i| i << 1}
p my_arr
pigeon%

pigeon% b.rb
[[1], [1], [1], [1]]
pigeon%

`<<' will modify the current element

Guy Decoux

[…]

my_arr = Array.new( 4 )
my_arr.each{ |i|
i = Array.new
}

my_arr.each { |j|
puts j.type
}

I get the following output:
NilClass
NilClass
NilClass
NilClass
[nil, nil, nil, nil]
[…]
Conclusion: I understand why I can’t pass Array.new as the default value in
Array.new. However, I do not understand why the first iterative approach
does not work. I obviously know how to acquire the desired result, but
that doesn’t quench my curiousity as to why the other does not work. Can I
bother someone for some enlightenment?

‘i’ is a reference that points to the array element. When you assign to it,
you make it point to your new array, and all contact with the array element
is lost. You need one more level of indirection.

Orion Hunter wrote:

My next approach was to create the array, with a dafault length, and iterate
over it assigning a new array to each element:

my_arr = Array.new( 4 )
my_arr.each{ |i|
    i = Array.new
}

my_arr.each { |j|
    puts j.type
}

I get the following output:
NilClass
NilClass
NilClass
NilClass
[nil, nil, nil, nil]

[…snip…]

Conclusion: I understand why I can’t pass Array.new as the default value in
Array.new. However, I do not understand why the first iterative approach
does not work. I obviously know how to acquire the desired result, but that
doesn’t quench my curiousity as to why the other does not work. Can I
bother someone for some enlightenment?

The iteration with ‘each’ yields a value, which is a reference to the
current object inside the Array at whatever index one is currently at.
It does not yield a mutable reference or pointer that allows
modification of the Array. On the first iteration with each
i == my_arr[0] # is true
but the ‘i’ variable is still an individual variable, with no link nor
knowledge of my_arr[0]. They just happen to point to the same object. I
think this constitutes “pass-by-value” semantics, while you are thinking
of “pass-by-reference” semantics.

What I would do in the above case, however, is:

(1…4).collect{ Array.new }

which fits better with the model you have in mind. It creates a new
array inserting the value returned from the block at the corresponding
position.
The variant collect! does it in-place, so perhaps

my_arr = Array.new( 4 )
my_arr.collect!{ Array.new }

would be faster (for larger values of 4, atleast :slight_smile:

HTH

···


([ Kent Dahl ]/)_ ~ [ http://www.stud.ntnu.no/~kentda/ ]/~
))_student
/(( _d L b_/ NTNU - graduate engineering - 5. year )
( __õ|õ// ) )Industrial economics and technological management(
_
/ö____/ (_engineering.discipline=Computer::Technology)

Conclusion: I understand why I can’t pass Array.new as the default value in
Array.new.

You can (in Ruby 1.8]), just use a block:

irb(main):009:0> a = Array.new(4) { Array.new }
=> [, , , ]
irb(main):010:0> a.size.times { |i| a[i] << i } ; a
=> [[0], [1], [2], [3]]

However, I do not understand why the first iterative approach does
not work. I obviously know how to acquire the desired result, but
that doesn’t quench my curiousity as to why the other does not work.
Can I bother someone for some enlightenment?

The i isn’t an alias for an array element unlike Perls $_. It’s just
another reference to this element, and you changed only this reference
to point somewhere else in your code.

You could use map instead pass the correctly initialized array to a:

irb(main):011:0> a = Array.new(4).map { Array.new }
=> [, , , ]
irb(main):012:0> a.size.times { |i| a[i] << i } ; a
=> [[0], [1], [2], [3]]

···

On 2003-05-01 00:22:47 +0900, Orion Hunter wrote: