Using .each with constructors

Is there a nice elegant way of creating several named objects of the
same class? I naively tried

a,b,c,d = 0
[a, b, c, d].each { |o| o = SomeClass.new }

and found that while they get initialised inside the block, they get
destroyed leaving it. I can’t believe

a = SomeClass.new
b = SomeClass.new
etc.

is the best way to do it. I can populate an array, but let’s
hypothesise that for reasons relating to irritating corporate coding
standards, the variables need specific names… :wink:

Thoughts appreciated! Cheers

On 7/20/06, Ben Z. [email protected] wrote:

is the best way to do it. I can populate an array, but let’s
hypothesise that for reasons relating to irritating corporate coding
standards, the variables need specific names… :wink:

I am sure some of the real Ruby hackers will have a better solution,
but you can do something like:

a,b,c = Array.new(3).map { Foo.new(42) }

pth

On Jul 20, 2006, at 1:35 PM, Ben Z. wrote:

Is there a nice elegant way of creating several named objects of the
same class? I naively tried

a,b,c,d = 0
[a, b, c, d].each { |o| o = SomeClass.new }

a, b, c, d = Array.new(4) { |i| String.new(i.to_s) }
=> [“0”, “1”, “2”, “3”]

Hope that helps.

James Edward G. II

Ben Z. wrote:

b = SomeClass.new
etc.

is the best way to do it. I can populate an array, but let’s
hypothesise that for reasons relating to irritating corporate coding
standards, the variables need specific names… :wink:

Thoughts appreciated! Cheers

The parallel assignment/array expansion approach suggested by patrick
and james will definitely work. You could also try

[:a,:b,:c,:d].each { |x| eval “#{x} = SomeClass.new” }

Hi –

On Fri, 21 Jul 2006, Mike H. wrote:

The parallel assignment/array expansion approach suggested by patrick and
james will definitely work. You could also try

[:a,:b,:c,:d].each { |x| eval “#{x} = SomeClass.new” }

That won’t work. The variables will be local to the eval, and not
available outside it.

David

Ben Z. wrote:

b = SomeClass.new
etc.

is the best way to do it. I can populate an array, but let’s
hypothesise that for reasons relating to irritating corporate coding
standards, the variables need specific names… :wink:

Thoughts appreciated! Cheers

Surely overkill but:

require 'facet/enumerable/every

a,b,c = ([SomeClass] * 3).every.new

T.

Mike H. wrote:

The parallel assignment/array expansion approach suggested by patrick
and james will definitely work. You could also try

[:a,:b,:c,:d].each { |x| eval “#{x} = SomeClass.new” }

I notice the first two can be combined to

a, b, c = Array.new(3) { SomeClass.new }

losing both the .map and the |i|, and that still works (go Ruby! I
continue to be amazed by how much it can work out…). But all of these
leave an anonymous array of the variables sitting around, which doesn’t
get GC’d. Can it be done without that, or should I reduce my pedantic
tendencies slightly and live with it? :wink:

Thanks for all the great responses!

On Jul 20, 2006, at 2:25 PM, Ben Z. wrote:

I notice the first two can be combined to

a, b, c = Array.new(3) { SomeClass.new }

losing both the .map and the |i|, and that still works

I was just using the i to produce different Strings so you could see
what was going on in the output. :wink:

James Edward G. II

Simon Kröger wrote:

btw i like

a, b, c = (1…3).map {Object.new}

Now that’s elegant. Why would you only use it for immutable types?

Ben Z. wrote

[…]
a, b, c = Array.new(3) { SomeClass.new }

losing both the .map and the |i|, and that still works (go Ruby! I
continue to be amazed by how much it can work out…). But all of these
leave an anonymous array of the variables sitting around, which doesn’t
get GC’d. Can it be done without that, or should I reduce my pedantic
tendencies slightly and live with it? :wink:

The array holds references to the new objects, but the objects don’t
know
that there is an array:


GC.start
p ObjectSpace.each_object(Array) {}
a, b, c = Array.new(3) { Object.new }
p ObjectSpace.each_object(Array) {}
GC.start
p ObjectSpace.each_object(Array) {}

output:
67
68
67

Thanks for all the great responses!

btw i like

a, b, c = (1…3).map {Object.new}

or

a, b, c = [42] * 3

but only for immutable types.

:slight_smile:

cheers

Simon

On 7/20/06, Ben Z. [email protected] wrote:

a = SomeClass.new
Ben

I prefer to write

class Class
def *(num)
a = []
num.times do
a << new
end
a
end
end

a, b, c = String * 3
###########
a << “hi”
puts a.inspect
puts b.inspect
puts c.inspect

too bad I cannot work out how to write

a, b, c = String * :many

Cheers
Robert

Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein

Ben Z. wrote:

Simon Kröger wrote:

btw i like

a, b, c = (1…3).map {Object.new}

Now that’s elegant. Why would you only use it for immutable types?

No, that’s ok for normal objects, but

a, b, c = [42] * 3

isn’t, because:

a, b, c = [“123”] * 3
b << ‘4’
puts a # => 1234

a, b and c are referencing the same object.

cheers

Simon

Mike H. wrote:

The parallel assignment/array expansion approach suggested by patrick
and james will definitely work. You could also try

[:a,:b,:c,:d].each { |x| eval “#{x} = SomeClass.new” }

You’re right, it works if you do the a=b=c=d=0 line first to init scope
outside the block. I tested with that and then forgot to include it.

[email protected] wrote:

       a << new

3.times { Object.new }

The solution i use to this is (1…3).map { Object.new }. It’s
elegant/concise enough for me

Robert D. wrote:

end
Hmm…It’s too bad that #times below returns 3. It be pretty neat if it
acted like #map in this respect instead.

3.times { Object.new }

Also

([lambda{Object.new}]*3).collect{|c| c.call }

and Robert’s solution lead me too:

class Proc
def *(i)
a = []
i.times{ a << call }
a
end
end

lambda{ Object.new } * 3

Which is pretty versitle. Unforunately I already have Proc#* tied up
with function composition.

T.

Mike H. wrote:

3.times { Object.new }

The solution i use to this is (1…3).map { Object.new }. It’s
elegant/concise enough for me

Sure. But it does have the additional overhead of creating a Range
object.

T.

On 7/20/06, [email protected] [email protected] wrote:

Which is pretty versitle. Unforunately I already have Proc#* tied up
with function composition.

There’s no conflict:

class Proc
def *(x)
if Integer===x
(0…x).map{call}
else
#…compose procs
proc{|*args| self[x[*args]]}
end
end
end

Caleb C. wrote:

else
   #...compose procs
   proc{|*args| self[x[*args]]}
end

end
end

Nice. I’ll use that.

Thanks!
T.

On Jul 20, 2006, at 7:32 PM, [email protected] wrote:

Hmm…It’s too bad that #times below returns 3. It be pretty neat
if it
acted like #map in this respect instead.

3.times { Object.new }

irb(main):001:0> RUBY_VERSION
=> “1.9.0”
irb(main):002:0> a, b, c = 3.times.map { Object.new }
=> [#Object:0x2382c8, #Object:0x2381d8, #Object:0x238070]

Just saying is all.

Hi –

On Fri, 21 Jul 2006, Logan C. wrote:

=> “1.9.0”
irb(main):002:0> a, b, c = 3.times.map { Object.new }
=> [#Object:0x2382c8, #Object:0x2381d8, #Object:0x238070]

Interesting… That reveals that one of the problems with this magic
enumerator thing is that the method names weren’t necessarily chosen
with this in mind, and don’t work very well. 3.times.map very
strongly does not communicate a 0…3 mapping to me. My first
reading is:

3.map

since I expect 3.times to return 3. My second reading is:

3.times { map { Object.new } }

which also makes no sense. It’s quite a leap to figure out that it
means:

(0…3).map { Object.new }

I fear the magic enumerator doesn’t sit very well with the language as
designed without it.

David