Using .each with constructors

B

Ben Zealley

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... ;)

Thoughts appreciated! Cheers
 
P

Patrick Hurley

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... ;)

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
 
J

James Edward Gray II

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 Gray II
 
M

Mike Harris

Ben said:
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... ;)

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" }
 
D

dblack

Hi --

Ben said:
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... ;)

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" }

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


David

--
http://www.rubypowerandlight.com => Ruby/Rails training & consultancy
http://www.manning.com/black => RUBY FOR RAILS (reviewed on
Slashdot, 7/12/2006!)
http://dablog.rubypal.com => D[avid ]A[. ]B[lack's][ Web]log
(e-mail address removed) => me
 
B

Ben Zealley

Mike said:
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? ;)

Thanks for all the great responses!
 
T

transfire

Ben said:
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... ;)

Thoughts appreciated! Cheers

Surely overkill but:

require 'facet/enumerable/every

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

T.
 
J

James Edward Gray II

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. ;)

James Edward Gray II
 
S

Simon Kröger

Ben Zealley 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? ;)

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.

:)

cheers

Simon
 
S

Simon Kröger

Ben said:
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
 
M

Mike Harris

Mike said:
Ben said:
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... ;)

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" }
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.
 
T

transfire

Robert said:
I prefer to write

class Class
def *(num)
a = []
num.times do
a << new
end
a
end
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.
 
M

Mike Harris

Robert Dober wrote:


I prefer to write

class Class
def *(num)
a = []
num.times do
a << new
end
a
end
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 }
The solution i use to this is (1..3).map { Object.new }. It's
elegant/concise enough for me
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.
 
C

Caleb Clausen

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.


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
 
T

transfire

Mike said:
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.
 
T

transfire

Caleb said:
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.


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

Nice. I'll use that.

Thanks!
T.
 
L

Logan Capaldo

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.
 
D

dblack

Hi --

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>]

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

--
http://www.rubypowerandlight.com => Ruby/Rails training & consultancy
http://www.manning.com/black => RUBY FOR RAILS (reviewed on
Slashdot, 7/12/2006!)
http://dablog.rubypal.com => D[avid ]A[. ]B[lack's][ Web]log
(e-mail address removed) => me
 
M

Martin DeMello

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>]

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

If you think about it, though, the underlying problem is that 3.times
passes a parameter into the block, which is what makes 3.times.map
{|i| } nonintuitive. The plain 3.times.map {} doesn't suggest a 0...3
mapping, but rather an enumerator consisting of three "passes" which
map naturally turns into a 3-object array. I predict that this *will*
seem intuitive once magic enumerators have been around a while - note
that it already reads perfectly as 3.times.collect

martin
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,209
Messages
2,571,088
Members
47,686
Latest member
sparada

Latest Threads

Top