Implementing the *ect methods in terms of inject
April 18, 2008
Josh told me today that one way to see if someone is any good at Ruby is to have them implement the *ect methods in terms of inject. This isn't a fool-proof method, and I'm also not concerned about it's screening value as much as I am simply interested in how to do it. Most importantly, do I meet Josh's standards?
(As Nick pointed out, anyone who's spent more than 5 minutes functional programming would laugh at this. I'm a newbie.)
Before I show my code, I'm going to give you the opportunity to go try it for yourself. Write custom #detect, #select, #reject, and #collect methods in terms of Enumerable#inject.
Here's a little RSpec example that will let you automatically verify your implementation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
describe Looper do before(:each) do @looper = Looper.new(1,2,3) end it "should detect" do @looper.detect {|i| i > 1 }.should == 2 end it "should select" do @looper.select {|i| i % 2 == 1 }.should == [1,3] end it "should reject" do @looper.reject {|i| i % 2 == 1 }.should == [2] end it "should collect" do @looper.collect {|i| i * 2 }.should == [2,4,6] end end |
And now for my implementation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class Looper def initialize(*args) @items = args end def detect @items.inject(nil) {|found, i| found ||= i if yield(i); found } end def select @items.inject([]) {|matches, i| matches << i if yield(i); matches } end def reject @items.inject([]) {|matches, i| matches << i unless yield(i); matches } end def collect @items.inject([]) {|matches, i| matches << yield(i) } end end |
How did you do it?
blog comments powered by Disqus
