hoodwink.d enhanced
RSS
2.0
XHTML
1.0

RedHanded

Enumerate Side-by-side with SyncEnumerator #

by why in inspect

Kenneth Kunz shined a light on the SyncEnumerator class, which comes with the standard library. A little bit of slightly life-changing code I’d say.

 require 'generator'

 numbers = (1..5)
 letters = ('a'..'c')

 enum = SyncEnumerator.new( numbers, letters )
 enum.each { |num, let| print "#{num},#{let} " }

Which prints: 1,a 2,b 3,c 4, 5,.

The each iterates through both collections simultaneously. You can pass any number of collections into SyncEnumerator. And, naturally, you can use any of the normal set of Enumerable methods. And you will.

 require 'generator'
 class Sync < SyncEnumerator
   def self.[]( *args ); new( *args ); end
 end

 Sync[numbers,letters].map { |n,l| "#{n} #{l}" }.join "\n" 

Update: You should definitely read the comments, noting the speed impairment incurred by using this generator, when compared to an equivalent technique starring Array#zip.

said on 14 Apr 2005 at 17:46

Which standard library?

said on 14 Apr 2005 at 17:47

Nevermind. Goofy mistake.

said on 14 Apr 2005 at 19:53

Does this post refer to another blog post? Link?

said on 14 Apr 2005 at 21:53

Oh, uh, it’s from Ruby-Talk.

said on 15 Apr 2005 at 00:56

I used it in the past, but it was dreadfully slow, because it used continuations. Maybe it changed now. At that time, i replaced my call with Array#zip which was an immediate performance boost. I saw once on ruby-talk someone offering a patch converting it to work without continuations but I don’t know if it was merged. Even if it was, beware, this can be mighty slow in some older rubys!

said on 15 Apr 2005 at 01:37

Emmanuel, I’m afraid its still slow. I’ve also personally switched to #zip, the cost of creating the temporary arrays seems to be less than using SyncEnumerator.

said on 15 Apr 2005 at 02:02

IIRC the patch was still using continuations, but in a somewhat different way. I don’t know if it was merged, though.

said on 15 Apr 2005 at 07:00

Array#zip need not generate a temporary Array anymore:

  ["a", "b", "c"].zip([1, 2, 3]) do |a, b|
    puts "#{a}: #{b}" 
  end
said on 16 Apr 2005 at 11:58

what is the syntax that lets you control whether to truncate longer input sequences, or pad shorter sequences (sorta like the difference betw zip(list1,list2) and map(None,list1,list2) in python?

said on 18 Apr 2005 at 04:14

ok, if the speed problem is still there, just to expand on it.

we are talking two orders of magnitude slower than Array#zip, minutes instead of split-seconds (at least in the cases where i tried). In my case maybe it got worse because my machine trashed (several hundreds of megs taken by ruby).

I posted on it there: http://www.ruby-talk.org/cgi-bin/vframe.rb/ruby/ruby-talk/105936?105721-106925+split-mode-vertical

said on 18 Apr 2005 at 16:00

OK, point taken… I hereby rescind my Ruby-Talk post, and I will dutifully zip from this day forward (my wife will be glad to hear this).

Unless I am working with arbitrary (non-Array) Enumerable collections and speed is not a consideration, in which case I may slip-in an occasional SyncEnumerator just for nostalgia.

Perhaps Rite (YARV) will address the performance problems?

said on 19 Apr 2005 at 12:03
The difference is that these continuation-based techniques are applicable in situations where #zip is not namely, when you have Enumarables you don’t want to turn into an arrays.

For example, Enumerables that yield infinite sequences, or perform costly calculations at each step, can’t be used with #zip.

said on 25 Apr 2005 at 12:28

My Pickaxe 2ed book says that #zip is provided by Enumerable, not Array. Just FYI .

Comments are closed for this entry.