Skip to content

Instantly share code, notes, and snippets.

@nicc
Created November 8, 2010 14:40
Show Gist options
  • Save nicc/667755 to your computer and use it in GitHub Desktop.
Save nicc/667755 to your computer and use it in GitHub Desktop.
Ruby's Enumerable #map and #select methods implemented with #inject
# The idea is just to illustrate that Ruby's #map and #select Enumerable methods
# are simply specific flavours of a fold operation, which Ruby implements as #inject.
# I've implemented #map and #select using #inject, and at the end I implemented #inject
# recursively. Ruby does #inject with Array#each, for very good reason. The recursive
# implementation is really just to illustrate the core Functional Programming concept
# which it derives from.
# We'll start simple. Map without applying a function:
[1,2,3,4,5].inject([]) {|memo, obj| memo << obj }
# => [1,2,3,4,5]
# Map that accepts a block argument (this can obv be implemented as an instance
# method on Enumerable. See #map. :P ):
def my_map(list)
list.inject([]) {|memo, obj| memo << yield(obj) }
end
my_map([1,2,3,4,5]) {|i| i * 2 }
# => [2, 4, 6, 8, 10]
# Select without a passed-in function (note how the recursive nature of the function
# is made clear by the return value of the block in the case where the predicate is not met):
[1,2,3,4,5].inject([]) {|memo, obj| obj > 3 ? memo << obj : memo }
# => [4,5]
# Select that accepts a function argument. Also easy to implement as an instance method:
def my_select(list)
list.inject([]) {|memo, obj| yield(obj) ? memo << obj : memo }
end
my_select([1,2,3,4,5]) {|i| i > 3 }
# => [4, 5]
# And just for fun lets implement inject recursively as #fold. You wouldn't want to do
# this in Ruby, the stack wouldn't handle long lists, and it destroys the list (pure
# FP is side-effect free). This simply illustrates the concept in Functional Programming,
# which is where these methods come from. In a pure FP language one would implement any
# method that acts on a list in this way (in relation to itself), but it wouldn't be as
# awkward to express.
def fold(current, list, &block)
return current if list.empty?
block.call(current, fold(list.shift, list, &block))
end
fold(0, [1,2,3,4]) {|memo, obj| memo + obj }
# => 10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment