Map/collect iterating over multiple arrays/arguments

Can I iterate over multiple arrays/collections?

It is very useful (and used in Lisp a lot) to map several lists onto
one, like map/collect but iterating over multiple collections at the
same time. For example:

foods   = ['banana', 'grass', 'peanuts']
animals = ['monkey', 'gnu',   'elephant']

[animals,foods].multi_each{|x,y| puts "#{x} eats #{y}" }

money eats banana
gnu eats grass
elephant eats peanuts

[animals,foods].multi_map{|x,y| "#{x} eats #{y}" }

=> [“money eats banana”, “gnu eats grass”, “elephant eats peanuts”]

This is relatively simple to implement using current Ruby features,
but at the cost of either zipping all input collections in memory, or
requiring that array implements integer indexing:

foods.zip(animals).each{|x,y| puts “#{x} eats #{y}” }

or

module Enumerable
def multi_map
result = []
self[0].each_index{|i|
args = self.collect{|x| x[i]}
result << yield(*args)
}
result
end
end

I assume this can also be done by using continuation, but is there a
simpler way. Are there Iterators for enumerables, like in Java?

–Laza

Zoran Lazarevic
212.569.4011
http://www.cs.columbia.edu/~laza

Simon Strandgaard wrote in message news:…

Can I iterate over multiple arrays/collections?
Are there Iterators for enumerables, like in Java?

Yes I have made some iterator classes… But its not yet released.

Simon, the iterator class uses indexing ( @data[@position] ) and that
is exactly what I tried to avoid. This does NOT work for collections
that do not support indexing operator (e.g. linked lists, iterating
through SQL resultset, etc.)

Apparently it is not possible to iterate using Collection.each over
multiple collections. It is possible by using one thread per
collection, but I do not want to go there.

–Laza

···

On Mon, 06 Oct 2003 17:17:55 -0700, Zoran Lazarevic wrote:

Zoran Lazarevic zoranlazarevic@yahoo.com skrev i en
nyhedsmeddelelse:32c0bb6a.0310071950.55c75dd0@posting.google.com

Simon Strandgaard wrote in message news:…

Can I iterate over multiple arrays/collections?
Are there Iterators for enumerables, like in Java?

Yes I have made some iterator classes… But its not yet released.

Simon, the iterator class uses indexing ( @data[@position] ) and that
is exactly what I tried to avoid. This does NOT work for collections
that do not support indexing operator (e.g. linked lists, iterating
through SQL resultset, etc.)

You can write an implicit iterator, like this:

cat snippet_implicit.rb
require ‘iterator’
class ImplicitIterator < Iterator::Base
def initialize
super()
first
end
def first; @value = 0 end
def next; @value += 1 end
def is_done?; @value >= 10 end
def current; @value end
end
i = ImplicitIterator.new
until i.is_done?
p i.current
i.next
end
ruby snippet_implicit.rb
0
1
2
3
4
5
6
7
8
9

Is this better ? :slight_smile:

Apparently it is not possible to iterate using Collection.each over
multiple collections. It is possible by using one thread per
collection, but I do not want to go there.

You can make an iterator which can iterate over multiple iterators at
the same time, like this:

require ‘iterator’
class MultiIterator < Iterator::Base
def initialize(*iterators)
@iterators = iterators
first
end
def first; @iterators.each{|i| i.first} end
def next; @iterators.each{|i| i.next} end
def is_done?
@iterators.each{|i| return true if i.is_done? }
false
end
def current; @iterators end
end

a = %w(a b c d e).create_iterator
b = (0…4).to_a.create_iterator
i = MultiIterator.new(a, b)
until i.is_done?
ia, ib = i.current
p [ia.current, ib.current]
i.next
end

Is this better ? … I hope :wink:

···

On Mon, 06 Oct 2003 17:17:55 -0700, Zoran Lazarevic wrote:


Simon Strandgaard

zoranlazarevic@yahoo.com (Zoran Lazarevic) wrote in message news:32c0bb6a.0310071950.55c75dd0@posting.google.com

Apparently it is not possible to iterate using Collection.each over
multiple collections. It is possible by using one thread per
collection, but I do not want to go there.

a continuations multi_each has been placed on the wiki at rubygarden.org
if you are interested

heres another one. it also defines an iterator class:

----CUT----
module Enumerable
class Iterator
public

attr_reader :enumerable, :has_next

def initialize enumerable, end_value = nil, &end_block
  @enumerable = enumerable
  @end_value = end_value
  @end_block = end_block
  initialize_fetch_block
end

def next
  @has_next ? fetch_next_element : fetch_end_value
end

def rewind
  initialize_fetch_block
  self
end

protected

def initialize_fetch_block
  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
  callcc do |@after_fetch| @next_fetch.call end
  @after_fetch = nil
  result
end

def fetch_end_value
  @end_block ? @end_block.call : @end_value
end

end

enumerable instance methods

def iterator; @iterator = Iterator.new self; end

enumerables class methods

class << self
def each(*enumerables, &block)
iterators = enumerables.collect{|e| e.iterator}
while true
args = iterators.collect{|i| i.next}
if args.detect{|arg| arg}
block.call *args
else
return enumerables
end
end
end
def collect(*enumerables, &block)
ret =
each(*enumerables){|*args| ret << (block.call *args)}
ret
end
alias map collect
end
end

if $0 == FILE
a = [‘fee’,‘fie’,‘foe’,‘fum’]
h = {‘k’ => ‘v’, ‘K’ => ‘V’}

ai = a.iterator
hi = h.iterator

while ((n = ai.next)) do
puts n.inspect
end

while ((n = hi.next)) do
puts n.inspect
end

Enumerable::each a, h do |elem, kv|
puts elem.inspect
puts kv.inspect
end

p Enumerable::collect(a,h){|e,kv| [e,kv]}
end

END

OUTPUT:

“fee”
“fie”
“foe”
“fum”
[“K”, “V”]
[“k”, “v”]
“fee”
[“K”, “V”]
“fie”
[“k”, “v”]
“foe”
nil
“fum”
nil
[[“fee”, [“K”, “V”]], [“fie”, [“k”, “v”]], [“foe”, nil], [“fum”, nil]]
----CUT----

-a

···

On Wed, 8 Oct 2003, quent wrote:

zoranlazarevic@yahoo.com (Zoran Lazarevic) wrote in message news:32c0bb6a.0310071950.55c75dd0@posting.google.com

Apparently it is not possible to iterate using Collection.each over
multiple collections. It is possible by using one thread per
collection, but I do not want to go there.

a continuations multi_each has been placed on the wiki at rubygarden.org
if you are interested

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

Ara Howard
NOAA Forecast Systems Laboratory
Information and Technology Services
Data Systems Group
R/FST 325 Broadway
Boulder, CO 80305-3328
Email: ara.t.howard@noaa.gov
Phone: 303-497-7238
Fax: 303-497-7259
The difference between art and science is that science is what we understand
well enough to explain to a computer. Art is everything else.
– Donald Knuth, “Discover”
~ > /bin/sh -c ‘for lang in ruby perl; do $lang -e “print "\x3a\x2d\x29\x0a"”; done’
====================================