How each class splats

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.

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.

Trans wrote:

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

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

···

On Mon, 26 Sep 2005, Trans wrote:

--
David A. Black
dblack@wobblini.net

Devin Mullins wrote:

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
=> #<struct A i=1, v=2>

arr = [A.new(1,2), A.new(3,4)]

=> [#<struct A i=1, v=2>, #<struct A i=3, v=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>, #<struct A i=3, v=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>, #<struct A i=3, v=4>]

arr.each {|*a| p a}

[#<struct A i=1, v=2>]
[#<struct A i=3, v=4>]
=> [#<struct A i=1, v=2>, #<struct A i=3, v=4>]

I guess this is the reason:

x,y=A.new(1,2)

=> [#<struct A i=1, v=2>]

x

=> #<struct A i=1, v=2>

y

=> nil

x,y=*A.new(1,2)

=> [1, 2]

x

=> 1

y

=> 2

x,y=[1,2]

=> [1, 2]

x

=> 1

y

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

:slight_smile:

Kind regards

    robert

Devin Mullins wrote:

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 '>>' :wink:

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.

Florian Groß wrote:

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.

Robert Klemme wrote:

Devin Mullins wrote:

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:
<snip>

Oooh... not what I meant. :slight_smile: 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/ }

Devin Mullins wrote:

Robert Klemme wrote:

Devin Mullins wrote:

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:
<snip>

Oooh... not what I meant. :slight_smile: 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?

a.class

=> ArrayOfB

a.class.ancestors

=> [ArrayOfB, #<Class:0x1017cc00>, Object, Kernel]

a.kind_of? Array

=> false

Array === a

=> false

Hm...

Kind regards

    robert

Robert Klemme wrote:

Devin Mullins wrote:

Oooh... not what I meant. :slight_smile: 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...)

a.kind_of? Array
     

=> false

Hm...

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

Devin

Devin Mullins wrote:

Robert Klemme wrote:

Devin Mullins wrote:

Oooh... not what I meant. :slight_smile: 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]

=> [#<Foo:0x1018b6c0>, #<Foo:0x1018b6a8>]

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]

=> [#<Bar:0x101c5f50>, #<Bar:0x101c5f38>]

a.each {|x,y| puts x,y}

1
2
1
2
=> [#<Bar:0x101c5f50>, #<Bar:0x101c5f38>]

I *knew* it had to be something simple. So Trans, that's the solution!

You understand DelegateClass, no?
(Probably much better than I do...)

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

Kind regards

    robert

Robert Klemme schrieb:

Mrh? ArrayOfB#each does the trick.

Can't be:

class Foo
  def each() yield 1; yield 2 end
end

Devin didn't define a special Foo#each, but ArrayOfFoo#each.

Regards,
Pit

Robert Klemme wrote:

I *knew* it had to be something simple. So Trans, that's the solution!

Sweet!

T.