Each_with_index & collect_with_index?

The problem is that Enumerable is built around a class-supplied ‘each’,
and each itself runs in a very black-box manner, communicating only
through yield. I don’t see how Enumerable would get the info, short of
enforcing an each_with_index requirement, and rewriting ‘each’ as a
wrapper around ‘each_with_index’.

Perhaps this might work (untested):

module Enumerable
alias _each each
def each
Enumerable.iteration = 0
_each {|*i|
Enumerable.iteration += 1
yield *i
}
end
end

but note that the number of times you’ve iterated is not necessarily the
same as what you’d want _with_index to return.

martin

···

Alan Chen alan@digikata.com wrote:

Well, how about something like Regexp.last_match, where you can
make a call to retrieve the iteration data.

ary.map { |e,f,g|
ei = Enumerable.iteration_info
#…
}

Hi –

OK… globals = evil… more importantly cryptic code
= evil. I can appreciate that. And I think getting
rid of the extraneous _with_index methods would clean
things up a bit.

Heh – I guess you’re not interested in membership in Citizens for MWI
(map_with_indices) :slight_smile:

But I’m still determined to come up with a solution :wink:

Can we arbitrarily add a parameter to the block?
Something like:

ary.collect {|a,b,c,:iteration| …}

The idea is if there is an argument beginning with
‘:’, assign the iteration value/object for this
particular block to that.

If you want to change the syntax of block variable names, you’ll have
to get in line and wait your turn :slight_smile: But the other problem with this
is that you’re introducing syntax change, just so you don’t have to
type “_with_index”. That’s not a good trade-off.

That way, block are still easily nestable. As for
your other comment… is it really necessary to have
an index associated with each argument? Shouldn’t
they all be synchronized?

In tinkering with this, the problem I was running into was with nested
arrays. Let’s say you define Enumerable#imap, which does a map
operation but also adds an “iteration” method to each object in the
receiver as it iterates.

a = [ [“one”, “two”], [“three”, “four”] ]
a.imap {|e| puts “we’re at #{e.iteration}”}

But now let’s say you do:

a.imap {|e,f| … }

which assigns the inner array elements to e and f. Now, somewhere
behind the scenes (in my version), you’re adding the #iteration method
to the actual objects being iterated. But here, the object – the
inner array – doesn’t actually appear as itself in the block; it’s
split across two variables. So you can’t say “e.iteration” or
“f.iteration”, because the original [e,f] array ([“one”,“two”]) is the
object that responds to “iteration”.

David

···

On Wed, 27 Nov 2002, Jason Persampieri wrote:


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

I like that, sort of. It still has the problem that a global does;
why not make it an importable method for Enumerable?

ary.map { |e, f, g|
ei = ary.iteration_info
#…
}

This doesn’t work in cases where the same Enumerable could be
iterated more than once (either by Threading or by nesting), but I
think that those are pathological cases anyway.

-austin
– Austin Ziegler, austin@halostatue.ca on 2002.11.27 at 09.11.52

···

On Wed, 27 Nov 2002 14:51:41 +0900, Alan Chen wrote:

On Wed, Nov 27, 2002 at 01:49:10PM +0900, > dblack@candle.superlink.net wrote:

So a new global of that kind probably isn’t on the horizon. I was
playing around with extending objects during iteration, so that
you could do:

ary.map {|e| puts “We’re at iteration #{e.iteration}”}

or something – but I haven’t come up with a way to do it that
fits more than a subset of cases. For one thing, you can’t extend
Fixnums, so anything based on that method won’t work if your
array contains those. Also, the syntax of block parameters makes
it hard: if you have

ary.map {|e,f,g| … }

I’m not sure there’s a way to determine which object got the
#iteration method added to it…
Well, how about something like Regexp.last_match, where you can
make a call to retrieve the iteration data.

ary.map { |e,f,g|
ei = Enumerable.iteration_info
#…
}

  def each
    Enumerable.iteration = 0

and if someone write :

   [[1, 2], [3, 4]].each {|i| i.each {|j| ... }}

Guy Decoux

Hi –

···

On Wed, 27 Nov 2002, Austin Ziegler wrote:

On Wed, 27 Nov 2002 14:51:41 +0900, Alan Chen wrote:

ary.map { |e,f,g|
ei = Enumerable.iteration_info
#…
}

I like that, sort of. It still has the problem that a global does;
why not make it an importable method for Enumerable?

ary.map { |e, f, g|
ei = ary.iteration_info
#…
}

This doesn’t work in cases where the same Enumerable could be
iterated more than once (either by Threading or by nesting), but I
think that those are pathological cases anyway.

Not necessarily… but also, given that all of this is to avoid
either *_with_index or a manually-incremented loop variable, it’s
probably not worth having it equate to a decision that certain other
constructs (threading or nesting) are to be avoided.

David


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

This doesn't work in cases where the same Enumerable could be
iterated more than once (either by Threading or by nesting), but I
think that those are pathological cases anyway.

Why it's pathologic to iterate on the same object in 2 different threads ?

Guy Decoux

I think this has been suggested before, but… why not use something like
Enumerable.index? But here’s the new bit, just have it refer to the innermost
iterator. You can assign it to something if you want nested iterators, eg:

arr.each do |e|
outerindex = Enumerable.index
arr.each do |f|
innerindex = Enumerable.index

end
end

And perhaps Enumerable.index == nil if we’re not inside an iterator.

Only problem I can see is if I were to define my own iterator using ‘yield’,
how would I indicate that I want Enumerable.index to return something
sensible?

Tim Bates

···

On Wed, 27 Nov 2002 11:20 pm, dblack@candle.superlink.net wrote:

Now, somewhere behind the scenes (in my version), you’re adding the
#iteration method to the actual objects being iterated.


tim@bates.id.au

[snippage]

which assigns the inner array elements to e and f. Now, somewhere
behind the scenes (in my version), you’re adding the #iteration method
to the actual objects being iterated. But here, the object – the
inner array – doesn’t actually appear as itself in the block; it’s
split across two variables. So you can’t say “e.iteration” or
“f.iteration”, because the original [e,f] array ([“one”,“two”]) is the
object that responds to “iteration”.

David,

Adding singletons to the iterator variable was
a path that I went down once… it had some
drawbacks, e.g., no Fixnums and overhead of
adding a singleton to each object.

Now that you’ve been playing with this some,
what are your thoughts on my “super-iterator”
from a few months back?

One of the problems it tried to solve was
subsuming #each_with_index into #each by
making every iterator object a special
object with an accessor method; if the
value changed, it would be stored back
in the original collection.

Someone will (correctly) protest that this
doesn’t replace every usage of #each_with_index.

But (personally) my most common use of it is when
I need to replace or change an element in an
array while I’m iterating.

Hal

···

----- Original Message -----
From: dblack@candle.superlink.net
To: “ruby-talk ML” ruby-talk@ruby-lang.org
Sent: Wednesday, November 27, 2002 6:50 AM
Subject: Re: each_with_index & collect_with_index?

Doh! What is the correct thing to do in such a case, without having to
introduce special syntax? Best I can think of is to have
Enumerable.iteration return a stack, which would be inconvenient to use.
Or maybe maintain a stack internally, but have a separate accessor for
the top of the stack (i.e. the innermost block count)

That might work out, actually

module Enumerable

def count
(@@iteration ||= ).last
end

def counts
@@iteration or
end

alias _each each
def each
(@@iteration ||= ).push(0)
_each {|*i|
@@iteration.last += 1
yield *i
}
@@iteration.pop
end
end

Another random thought: can you get at the names of the block local
variables? Might be possible to hack something up along the lines of a
hash of symbols, if so.

martin

···

ts decoux@moulon.inra.fr wrote:

def each
Enumerable.iteration = 0

and if someone write :

[[1, 2], [3, 4]].each {|i| i.each {|j| … }}

arr.each do |e|
outerindex = Enumerable.index
arr.each do |f|
innerindex = Enumerable.index

end
end

this is all very straight forward using an Enumerable::Iterator, i had to add
only two lines to the class to accomplish this :

arr = %w(zero one two)
a = arr.iterator
a.each do |ea|
print “a.index(#{a.index}) → ea : #{ea}\n\t”
b = arr.iterator
b.each do |eb|
print "b.index(#{b.index}) → eb : #{eb}, "
end
print “\n”
end

a.index(0) → ea : zero
b.index(0) → eb : zero, b.index(1) → eb : one, b.index(2) → eb : two,
a.index(1) → ea : one
b.index(0) → eb : zero, b.index(1) → eb : one, b.index(2) → eb : two,
a.index(2) → ea : two
b.index(0) → eb : zero, b.index(1) → eb : one, b.index(2) → eb : two,

http://eli.fsl.noaa.gov/lib/ruby/site_ruby/1.6/enumerable.rb

-a

···

On Wed, 27 Nov 2002, Tim Bates wrote:

====================================

Ara Howard
NOAA Forecast Systems Laboratory
Information and Technology Services
Data Systems Group
R/FST 325 Broadway
Boulder, CO 80305-3328
Email: ahoward@fsl.noaa.gov
Phone: 303-497-7238
Fax: 303-497-7259
====================================

Hi –

From: dblack@candle.superlink.net
To: “ruby-talk ML” ruby-talk@ruby-lang.org
Sent: Wednesday, November 27, 2002 6:50 AM
Subject: Re: each_with_index & collect_with_index?

[snippage]

which assigns the inner array elements to e and f. Now, somewhere
behind the scenes (in my version), you’re adding the #iteration method
to the actual objects being iterated. But here, the object – the
inner array – doesn’t actually appear as itself in the block; it’s
split across two variables. So you can’t say “e.iteration” or
“f.iteration”, because the original [e,f] array ([“one”,“two”]) is the
object that responds to “iteration”.

David,

Adding singletons to the iterator variable was
a path that I went down once… it had some
drawbacks, e.g., no Fixnums and overhead of
adding a singleton to each object.

Now that you’ve been playing with this some,
what are your thoughts on my “super-iterator”
from a few months back?

Yes, I was reminiscing about that, and realizing that I was
semi-reinventing it :slight_smile: But I just can’t find the right “home” for an
internal iteration counter. Extending the object has so many
problems, and the various global techniques are, well, global.

One thing I believe is missing, conceptually speaking, is the ability
on the part of a proc or code block to capture a reference to itself
from inside itself. If it could, then each block could be extended to
have a counter for itself. I think. Maybe. That’s the only avenue I
can think of to go down which doesn’t feel like it’s stacking too much
info into too little namespace. Then again, it might not be thread
safe. But it could report out its counter based on a thread-based
hash…

David

···

On Thu, 28 Nov 2002, Hal E. Fulton wrote:

----- Original Message -----


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

module Enumerable

I don't think that you can put it in a module, or a class.
             

Another random thought: can you get at the names of the block local
variables? Might be possible to hack something up along the lines of a
hash of symbols, if so.

#local_variables give you the name of all local variables (with block
  local) but you'll have a problem (like your current implementation) when
  I'll introduce threads :-))

Guy Decoux

[snip]

One thing I believe is missing, conceptually speaking, is the ability
on the part of a proc or code block to capture a reference to itself
from inside itself.
[snip]

is this not what this does?

[snip]
def initialize_fetch_block
@index = -1
callcc do |@after_fetch|
@has_next = true
@enumerable.each do |@next_element|
callcc do |@next_fetch| @after_fetch.call end
end
@has_next = false
@next_fetch = nil
@after_fetch.call
end
@after_fetch = nil
end

def fetch_next_element
  result = @next_element
  @index = @index + 1
  callcc do |@after_fetch| @next_fetch.call end
  @after_fetch = nil
  result
end

[snip]

@next_fetch and @after_fetch are referencess to blocks captured from inside
blocks?

-a

···

On Thu, 28 Nov 2002 dblack@candle.superlink.net wrote:

====================================

Ara Howard
NOAA Forecast Systems Laboratory
Information and Technology Services
Data Systems Group
R/FST 325 Broadway
Boulder, CO 80305-3328
Email: ahoward@fsl.noaa.gov
Phone: 303-497-7238
Fax: 303-497-7259
====================================

module Enumerable

I don’t think that you can put it in a module, or a class.

Where would I put it, then? Global is obviously out, and I can’t think
of any object I could attach the countstack to. Maybe an instance of a
CountStack class, with Kernel methods to access the stack.

Another random thought: can you get at the names of the block local
variables? Might be possible to hack something up along the lines of a
hash of symbols, if so.

#local_variables give you the name of all local variables (with block
local) but you’ll have a problem (like your current implementation) when
I’ll introduce threads :-))

Ah, threads :slight_smile: Is there a way of writing a class so that I can have only
one CountStack object per thread, but each thread has its own instance?

martin

···

ts decoux@moulon.inra.fr wrote: