Created
December 10, 2013 22:28
-
-
Save kerryb/7901501 to your computer and use it in GitHub Desktop.
Ruby blocks, procs, map and reduce
This file contains hidden or 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
# A proc is just a block of code that can be called later: | |
say_hello = -> { 2 + 2 } | |
say_hello.call # => 4 | |
# Procs can take parameters: | |
double = ->(x) { x * 2 } | |
double.call(2) # => 4 | |
# These are all identical: | |
double = ->(x) { x * 2 } | |
double = Proc.new {|x| x * 2 } | |
double = proc {|x| x * 2 } | |
double = proc do |x| | |
x * 2 | |
end | |
# This one is almost the same, but not quite: | |
double = lambda {|x| x * 2 } | |
# There are some synonyms for call: | |
double.call 2 # => 4 | |
double[2] # => 4 | |
double.(2) # => 4 | |
# A method can take a proc as an argument: | |
def do_twice argument, function | |
function.call(function.call(argument)) | |
end | |
do_twice 2, double # => 8 | |
# But more commonly the block is provided explicitly: | |
def do_twice argument, &block | |
block.call(block.call(argument)) | |
end | |
do_twice(2) {|x| x * 2 } # => 8 | |
# Or if you prefer: | |
do_twice 2 do |x| | |
x * 2 | |
end | |
# Collections (arrays etc) have lots of methods that take blocks (mostly from | |
# the Enumerable module). The simplest is each: | |
first_array = [1, 2, 3, 4] | |
second_array = [] | |
first_array.each do |a| | |
second_array << a * 2 | |
end | |
second_array # => [2, 4, 6, 8] | |
# But that's pretty clumsy when we want to transform one array into another | |
# with the same number of elements. Enter map: | |
first_array.map {|a| a * 2 } # => [2, 4, 6, 8] | |
# If all we're doing is calling a method on each element, there's a shorthand | |
# version using the &: syntax. These two are equivalent: | |
["hello", "world"].map {|word| word.upcase } # => ["HELLO", "WORLD"] | |
["hello", "world"].map(&:upcase) # => ["HELLO", "WORLD"] | |
# (As an aside, this works because preceding a value with & means ruby calls | |
# the object's to_proc method, and to_proc on a symbol (eg :upcase) returns a | |
# proc with one argument that sends the symbol as a message to the argument | |
# (which basically means calling the method with that name). | |
# | |
# Here's the implementation that Rails provided to make this work, back before | |
# Symbol#to_proc was added to Ruby itself: | |
class Symbol | |
def to_proc # !> method redefined; discarding old to_proc | |
proc { |obj, *args| obj.send(self, *args) } | |
end | |
end | |
# Sometimes we want to reduce an array down to a single element. This is a job | |
# for reduce (also called inject). Its block takes the current element and an | |
# accumulator value, and returns the accumulator each time. You can give it an | |
# initial value: | |
first_array.reduce(0) {|element, accumulator| accumulator + element } # => 10 | |
# Stepping through each call to the block, we would see: | |
0 + 1 # => 1 | |
1 + 2 # => 3 | |
3 + 3 # => 6 | |
6 + 4 # => 10 | |
# If you don't give it an initial value, it starts with the first element and | |
# goes from there: | |
first_array.reduce {|element, accumulator| accumulator + element } # => 10 | |
# Again, you can use &: if you're just calling a method on the accumulator andpassing it the element | |
first_array.inject(&:+) # => 10 | |
# In case you wondered, the + operator is actually a method on numeric classes: | |
2 + 3 # => 5 | |
2.+(3) # => 5 | |
# Have a look at the documentation for Enumerable for more neat stuff to do with lists! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment