Array#next - Enumerable#next?

rubyists-

does anyone know a way to implement this generically for all enumerable
objects in an efficient manner?

class Array
def next
# start iterating if we never have before
@itr = 0 unless @itr
@itr = @itr.succ

  # if there are no more elements return nil
  # but also set itr back to zero so subsequent
  # calls to next will succeed
  if @itr > size
@itr = 0
return nil
  end

  return self[@itr - 1]
end

end

this is really usefull. if it could be done for any Enumerable object then
something like this :

module Kernel
def concert(*enumerables, &block)
args = enumerable.collect{|e| e.next}
return if args.include? nil
block.call *args
end
end

h = {:answer => 42}
a = [42]

concert(h, a) do |k,v,a|
puts k, v, a
end

would be very cool. also, this could be more generic than some of the
methods being suggested for things like this…

-a

···

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

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,

does anyone know a way to implement this generically for all enumerable
objects in an efficient manner?

Scheme 1)

Use “to_a” to convert Enumerable into Array first.
Easiest way.

Scheme 2)

Use thread to save/restore “each” iteration.

Scheme 3)

Use continuation to save/restore “each” iteration.

Hint, here’s my “generator.rb” which uses scheme 3). You can start
from this sample.

						matz.
···

In message “Array#next - Enumerable#next ?” on 02/11/15, ahoward ahoward@fsl.noaa.gov writes:

class Generator
def initialize(method, *args)
@cont_l = nil
method.call(*args) {|v|
callcc {|c|
@cont_i = c
@value = v
return unless @cont_l
@cont_l.call
}
}
@cont_i = nil
@cont_l.call
end
def next
unless @cont_i
raise “no more element”
end
v = @value
callcc{|cc|
@cont_l = cc
@cont_i.call
}
v
end
def more?
if @cont_i then true else false end
end
end

class Object
def generator(method, *args)
Generator.new(self.method(method), *args)
end
end

if $0 == FILE
def zip(*args)
generators = args.collect{|x| x.generator(:each)}
ary =
while generators.any?{|x| x.more?}
ary.push(generators.collect{|x| x.next rescue nil})
end
ary
end

a = 1…10
b = “a”…“z”
for x,y in zip(a,b)
p [x,y]
end
end

One problem here is that I can’t tell the difference between iterating
over a nil object in an array (e.g. if I have an array [1, 2, 3, nil,
4]) and hitting the end of the array. This is pretty easy to fix;
change:
return nil

to:
return DoneIterating

A more serious problem here is that code that uses next() is not
reentrant; if you have a method that is iterating over an array, you’d
better not call another method that also iterates over that array.
E.g.:

def print_array(arr)
while (obj = arr.next) != DoneIterating do
puts obj
end
end

def print_all_ocurrances(arr, x)
while (obj = arr.next) != DoneIterating do
if x == obj then
print "Found object x in array "
print_array arr
puts
end
end
end

does not work as expected. To work around this, you should instantiate
an iterator object whenever you want to iterate over your array.

rubycollections has an iterator library for doing this generically:

RubyCollections download | SourceForge.net

You can use it like this:
arr = [1, 2, 3, 4, 5]
it = GenericIterator.new(arr)
while not it.finished? do
p it.elem
it.advance
end

though I regret that I haven’t had time to work on this project as much
as I would like (one idea that was proposed was to merge my iterators
with Horste Duchene’s streams, but that’s a big project). You might
also want to look at:

http://www.rubygarden.org/article.php?sid=107
http://www.ruby-lang.org/cgi-bin/cvsweb.cgi/rough/lib/generator.rb?rev=1.6&content-type=text/x-cvsweb-markup
http://www.ruby-lang.org/en/raa-list.rhtml?name=stream.rb

for some alternate implementations.

Paul

···

On Fri, Nov 15, 2002 at 05:40:58AM +0900, ahoward wrote:

class Array
def next
# start iterating if we never have before
@itr = 0 unless @itr
@itr = @itr.succ

  # if there are no more elements return nil
  # but also set itr back to zero so subsequent
  # calls to next will succeed
  if @itr > size

@itr = 0
return nil
end

  return self[@itr - 1]
end

end

One problem here is that I can’t tell the difference between iterating
over a nil object in an array (e.g. if I have an array [1, 2, 3, nil,
4]) and hitting the end of the array. This is pretty easy to fix;
change:
return nil

to:
return DoneIterating
[snip]

yes. this is a good idea

[snip]

A more serious problem here is that code that uses next() is not
reentrant; if you have a method that is iterating over an array, you’d
better not call another method that also iterates over that array.
E.g.:

def print_array(arr)
while (obj = arr.next) != DoneIterating do
puts obj
end
end

def print_all_ocurrances(arr, x)
while (obj = arr.next) != DoneIterating do
if x == obj then
print "Found object x in array "
print_array arr
puts
end
end
end
[snip]

i don’t view this as a problem - you are viewing the next method as an
iterator, which in definitively is not. it is an instance method of Array,
and uses instance variables. i would define Array#next as the the next
object the Array is willing to give me. this relies on instance variables,
just like Array#size. if you call a method manipulating Array#size, from
another method manipulating Array#size - would you be suprised that size had
changed? this is good argument for iterator classes i suppose : one needs a
separte object for tracking state. however, i’ve been using that method quite
a bit - it’s usefull because it’s minimal.

i will check out the links you sent. thanks.

-a

···

On Fri, 15 Nov 2002, Paul Brannan 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
====================================

Fri, 15 Nov 2002 10:41:45 +0900, ahoward ahoward@fsl.noaa.gov pisze:

i don’t view this as a problem - you are viewing the next method as an
iterator, which in definitively is not. it is an instance method of Array,
and uses instance variables. i would define Array#next as the the next
object the Array is willing to give me.

It puts the state of an iteration over the array inside the array,
which is evil, even if looks convenient. Traps:

  • Iteration within another iteration.
  • Iteration from different threads.
  • Aborting an iteration early and then starting it again without
    resetting the iteration state.

if you call a method manipulating Array#size, from another method
manipulating Array#size - would you be suprised that size had
changed?

No, because I’m changing the array. But in the above I’m not changing
the array, only iterate over it. In other words it turns a non-destructive
operation of iterating over the array into a destructive operation which
modifies the array.

···


__("< Marcin Kowalczyk
__/ qrczak@knm.org.pl
^^ http://qrnik.knm.org.pl/~qrczak/

Kinda old question, but anyway – does anyone have any luck in
installing and running mod_ruby and eruby on MacOS X 10.2? On my system
httpd reports a bunch of unresolved ruby symbols, such as rb_cObject
and others.

Thank you,
Gennady.