Created
November 8, 2010 14:40
-
-
Save nicc/667755 to your computer and use it in GitHub Desktop.
Ruby's Enumerable #map and #select methods implemented with #inject
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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