map/collect iterating over multiple arrays/arguments

  • Thread starter Zoran Lazarevic
  • Start date
Z

Zoran Lazarevic

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}
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
 
S

Simon Strandgaard

Can I iterate over multiple arrays/collections?

server> ruby dual.rb
["a", 1]
["b", 2]
["c", 3]
["d", 4]
["e", 5]
server> cat dual.rb
require 'iterator'

a = %w(a b c d e).create_iterator
b = (1..5).to_a.create_iterator
until a.is_done?
p [a.current, b.current]
a.next
b.next
end
server>


[snip]
Are there Iterators for enumerables, like in Java?

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

You can get the 'iterator.rb' file here:
http://rubyforge.org/cgi-bin/cvsweb...ent-type=text/x-cvsweb-markup&cvsroot=aeditor
 
Z

Zoran Lazarevic

Simon Strandgaard wrote in message news:...
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
 
S

Simon Strandgaard

Zoran Lazarevic said:
Simon Strandgaard wrote in message news:...
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:

server> 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
server> ruby snippet_implicit.rb
0
1
2
3
4
5
6
7
8
9
server>

Is this better ? :)

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 ;-)
 
Q

quent

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
 
A

Ara.T.Howard

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
====================================
| Ara Howard
| NOAA Forecast Systems Laboratory
| Information and Technology Services
| Data Systems Group
| R/FST 325 Broadway
| Boulder, CO 80305-3328
| Email: (e-mail address removed)
| 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'
====================================
 

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,767
Messages
2,569,571
Members
45,045
Latest member
DRCM

Latest Threads

Top