How each class splats

T

Trans

First I'll just ask it there's a way to do this WITHOUT redefining
#each in Array.

class A
def initialize(i,v)
@i,@v=i,v
end
end

arrayofA = [ A.new:)a,1), A.new:)b,2) ]

arrayofA.each { |e| p e }
=> #<A @i=:a,@v=1>
#<A @i=:b,@v=2>

arrayofA.each { |i,v| p i,v }
=> :a
1
:b
2

If thsi is not possible, what do people think of allowing a class to
define a #splat method to determine how it will splat into an each
block (and perhaps other things).

class A
def initialize(i,v)
@i,@v=i,v
end
def splat( arity )
case arity
when 1 then self
when 2 then [@i,@v]
else
[@i,@v].concat( [nil]*(arity-2) )
end
end
end

Or something to that effect.

Thanks,
T.
 
D

Devin Mullins

Well, you could do this:

class B < Array
def initialize(i,v)
self[0] = @i = i
self[1] = @v = v
end
end

But I think your best bet is define your own Collection object with its
own #each.

Devin

Interesting sidenote (endnote?): Operator precedence causes this:
self[0], self[1] = @i, @v = i, v
To set self[1] = @i, and @v = i, and to return the Array containing
self[0], self[1], @v, and v. If you want to do a silly thing like chain
parallel assignments, you have to add parens:
self[0], self[1] = (@i, @v = i, v)
But then again, that's what you get for trying to write code that's
painful to read.
 
R

Robert Klemme

Devin said:
Well, you could do this:

class B < Array
def initialize(i,v)
self[0] = @i = i
self[1] = @v = v
end
end

But I think your best bet is define your own Collection object with
its own #each.

I though so, too, but it doesn't help:

13:13:33 [~]: irbs
A = Struct.new:)i, :v) => A
A.ancestors => [A, Struct, Enumerable, Object, Kernel]
A.new(1,2).each {|x| p x}
1
2
=> # said:
arr = [A.new(1,2), A.new(3,4)]
arr.each {|a,b| puts a,b}
#<struct A i=1, v=2>
nil
#<struct A i=3, v=4>
nil
#<struct A i=1, v=2>
nil
#<struct A i=3, v=4>
nil
[#<struct A i=1, v=2>]
[#<struct A i=3, v=4>]
[QUOTE="# said:
x,y=A.new(1,2)
=> [# said:
=> # said:
y => nil
x,y=*A.new(1,2) => [1, 2]
x => 1
y => 2
x,y=[1,2] => [1, 2]
x => 1
y
[/QUOTE]
=> 2

In other words, it's not sufficient to be Enumerable to automatically be
assigned to individual variables in this scenario. You must have an
Array. Maybe to change this is worth an RCR?

While I would find this convenient it's really not too hard to access
members explicitely.
Interesting sidenote (endnote?): Operator precedence causes this:
self[0], self[1] = @i, @v = i, v
To set self[1] = @i, and @v = i, and to return the Array containing
self[0], self[1], @v, and v. If you want to do a silly thing like
chain parallel assignments, you have to add parens:
self[0], self[1] = (@i, @v = i, v)
But then again, that's what you get for trying to write code that's
painful to read.

:)

Kind regards

robert
 
F

Florian Groß

Trans said:
First I'll just ask it there's a way to do this WITHOUT redefining
#each in Array.

class A
def initialize(i,v)
@i,@v=i,v
end
end

arrayofA = [ A.new:)a,1), A.new:)b,2) ]

arrayofA.each { |e| p e }
=> #<A @i=:a,@v=1>
#<A @i=:b,@v=2>

arrayofA.each { |i,v| p i,v }
=> :a
1
:b
2

Try this:
class A
def initialize(i,v)
@i, @v = i, v
end

def to_a()
[@i, @v]
end
end

ary = [A.new:)a, 1), A.new:)b, 2)]
ary.each { |a| i, v = *a; p i, v }

In theory .each { |(i, v)| ... } should also work, but it seems that
Ruby doesn't call .to_a there. (I think it should do so.)
 
D

David A. Black

Hi --

First I'll just ask it there's a way to do this WITHOUT redefining
#each in Array.

class A
def initialize(i,v)
@i,@v=i,v
end
end

arrayofA = [ A.new:)a,1), A.new:)b,2) ]

arrayofA.each { |e| p e }
=> #<A @i=:a,@v=1>
#<A @i=:b,@v=2>

arrayofA.each { |i,v| p i,v }
=> :a
1
:b
2

You could probably get a similar result by doing something with
A#inspect, though probably not exactly what you've got here.
If thsi is not possible, what do people think of allowing a class to
define a #splat method to determine how it will splat into an each
block (and perhaps other things).

I'd want a better name, and more rationale. My instinct is to prefer
to know how scalar and/or container objects will behave, and plan for
that, rather than have different objects do arbitrarily different
things in the face of argument assignment.


David
 
D

Devin Mullins

Robert said:
Devin Mullins wrote:


I though so, too, but it doesn't help:
<snip>
Oooh... not what I meant. :) I meant something like this:

class B
attr_reader :i, :v
def initialize(i,v)
@i, @v = i, v
end
end

class ArrayOfB < DelegateClass(Array)
def each
super { |b| yield b.i, b.v }
end
end

a = ArrayOfB.new [B.new:)a, 1), B.new:)b, 2)]
a.each { |i, v| puts "i = #{i.inspect}, v = #{v.inspect}" }
In other words, it's not sufficient to be Enumerable to automatically be
assigned to individual variables in this scenario. You must have an
Array. Maybe to change this is worth an RCR?
Definitely not .include? Enumerable, since not all Enumerable methods
require finity (finiteness? finition? finality?)*. Maybe against .to_a,
though, but its deprecatedness (deprecatedity? deprecatality?
deprecation? aha! deprecation!) on Object makes me wonder if we aren't
heading down a path matz & friends already scoped out and nixed..
While I would find this convenient it's really not too hard to access
members explicitely.
Ditto.

Devin.

*Point in case:

class Fibs
extend Enumerable
def self.each
i, j = 0, 1
loop do
yield i
i, j = j, i + j
end
end
end

#find the first Fibonacci number with two nines in a row
Fibs.find { |i| i.to_s =~ /99/ }
 
T

Trans

Devin said:
Well, you could do this:

class B < Array
def initialize(i,v)
self[0] = @i = i
self[1] = @v = v
end
end

But I think your best bet is define your own Collection object with its
own #each.

Nah, that ruins the whole point. In my case I'm tryinig to create a
general pupose "association" class. So I can do:

a >> b

Same as

Assoc.new(a,b)

Please ignore that I'm overridding '>>' ;)

So then I do:

[ a0 >> b0, a1 >> b1, a2 >> b2 ]

And have an object much like an ordered hash. But I also can create a
mixed collection if I so need:

[ a0 >> b0, c0, a1 >> b1, c1, a2 >> b2, c2 ]

But I'm more interested in the ordered-hashish at this point. I want to
use it as one normally would use a hash, but alas I don't see any way
to do that expect to overide certain methods in Array, especailly
#each.

So I started thinking how a class might have some generalizing methods
to allow this to work.

T.
 
T

Trans

Florian said:
In theory .each { |(i, v)| ... } should also work, but it seems that
Ruby doesn't call .to_a there. (I think it should do so.)

Ah, that would be an acceptable solution. I wish it did so too.

T.
 
R

Robert Klemme

Devin said:
Robert said:
Devin Mullins wrote:


I though so, too, but it doesn't help:
<snip>
Oooh... not what I meant. :) I meant something like this:

class B
attr_reader :i, :v
def initialize(i,v)
@i, @v = i, v
end
end

class ArrayOfB < DelegateClass(Array)
def each
super { |b| yield b.i, b.v }
end
end

a = ArrayOfB.new [B.new:)a, 1), B.new:)b, 2)]
a.each { |i, v| puts "i = #{i.inspect}, v = #{v.inspect}" }

Interesting. Why does it work? Which method does the trick here?
=> false

Hm...

Kind regards

robert
 
D

Devin Mullins

Robert said:
Devin Mullins wrote:

Oooh... not what I meant. :) I meant something like this:

class B
attr_reader :i, :v
def initialize(i,v)
@i, @v = i, v
end
end

class ArrayOfB < DelegateClass(Array)
def each
super { |b| yield b.i, b.v }
end
end

a = ArrayOfB.new [B.new:)a, 1), B.new:)b, 2)]
a.each { |i, v| puts "i = #{i.inspect}, v = #{v.inspect}" }
Interesting. Why does it work? Which method does the trick here?
Mrh? ArrayOfB#each does the trick. You understand DelegateClass, no?
(Probably much better than I do...)
=> false


Hm...
Yes, that's the downside, but it's not necessarily much of one, is it?

Devin
 
R

Robert Klemme

Devin said:
Robert said:
Devin Mullins wrote:

Oooh... not what I meant. :) I meant something like this:

class B
attr_reader :i, :v
def initialize(i,v)
@i, @v = i, v
end
end

class ArrayOfB < DelegateClass(Array)
def each
super { |b| yield b.i, b.v }
end
end

a = ArrayOfB.new [B.new:)a, 1), B.new:)b, 2)]
a.each { |i, v| puts "i = #{i.inspect}, v = #{v.inspect}" }
Interesting. Why does it work? Which method does the trick here?
Mrh? ArrayOfB#each does the trick.

Can't be:
class Foo
def each() yield 1; yield 2 end
end => nil
a=[Foo.new, Foo.new]
a.each {|x,y| puts x,y}
#<Foo:0x1018b6c0>
nil
#<Foo:0x1018b6a8>
nil
=> [#<Foo:0x1018b6c0>, #<Foo:0x1018b6a8>]

It must be something different.

Ah! I remember: it's #to_ary:
class Bar
def to_ary() [1,2] end
end => nil
a=[Bar.new, Bar.new]
a.each {|x,y| puts x,y}
1
2
1
2
#<Bar:0x101c5f38>] said:
You understand DelegateClass, no?
(Probably much better than I do...)

I know and understand it. I dunno whether better or worse than you. :)

Kind regards

robert
 

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

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top