Thursday, October 05, 2006

Looking for cumulation in Ruby?

Module: Enumerable

This is pretty basic, but I only recently discovered it. I have a task that I do often in code. Usually, there is some list of things that I'd need to go through, and tack it on to another list if it meets some condition.

funny_posts = []
posts.each do |post|
if post.is_funny?
funny_posts << post

The same problem appears when I try to summate all the things that fit some condition. I dislike having the initialization there in the beginning. Is there a better/prettier way? I would have thought collect() would be it, but it is mapping one value for another. It's almost like a function in math--a certain input gives you a certain output.

So it was finally that I saw inject(), and it seems to be what I want.

funny_posts = posts.inject([]) do |funny_posts_thus_far, post|
if post.is_funny?
funny_posts_thus_far << post

I think that'll work. The difference doesn't look like much, but somehow it bothers me when I have that floating assignment before the loop. It's easy during maintainance that someone moves 'funny_posts=[]' far away from the loop, when semantically, it's a part of how the loop will work correctly.

inject() also applied to cases where you're trying to find the maximum in a list.

# find the longest word
longest = %w{ cat sheep bear }.inject do |memo,word|
memo.length > word.length ? memo : word
longest #=> "sheep"

I guess it's the difference between functional programming and procedural...using return of value instead of relying on side effects. I use to write off functional programming as something that was antiquited, but now, I'm finding gems here and there in functional programming. It makes me wonder how procedural took off so rapidly. Perhaps programmers think easier in procedural programming?

Update: I guess I should read Enumerable more closely. The example I had with collecting funny posts is done with a method called partition() in Enumerables.

No comments:

Post a Comment