Thursday, August 02, 2007

Parallel array processing with zip()

When I'm working in Ruby, it's a blast, but a lot of the concepts are borrowed from functional style programming, and I'm not sure what things are called. Often times, I end up writing the same boiler plate code over and over again, refactoring it into a function that I add to the standard library on my own, while knowing there's probably a built-in function to do that. It was like that with inject(). Today, I discovered zip().

I run into this particular problem when I want to line up arrays with one another, and perform an operation on the same corresponding element of each array--much like a parallel map function. For example, vector addition and multiplication is like this. When you add arrays, you take the the nth element from each vector and add them together.

Usually, I write something like this:
a,b = [1,2,3], [2,3,4]
class Array
def parallel_map(b)
result = []
each_with_index do |e, i|
result << yield e, b[i]

a.parallel_map(b) { |ea, eb| ea + eb }

Which is pretty ugly, as I shamefully admit. However, due to the post I last mentioned about Erlang, as well as this post on Ruby talk, I (finally) made the connection that there's such a function in ruby, and it's (also) called zip().

The solution then would look something like this: { |i, j| i + j }

MUCH better. That means something like dot_product is a lot more elegant now.
def dot_prod(a, b) { |i, j| i + j }.inject { |t, e| t += e }

To be honest, I've read the reference API on Ruby's enumerable, array, and hash libraries before. However, at the time that I read zip(), I was thinking, "I'm not sure when I'll ever need to do that.", and just put it out of my mind. :P

No comments:

Post a Comment