Skip to content

Instantly share code, notes, and snippets.

@kerryb
Created December 10, 2013 22:28
Show Gist options
  • Save kerryb/7901501 to your computer and use it in GitHub Desktop.
Save kerryb/7901501 to your computer and use it in GitHub Desktop.
Ruby blocks, procs, map and reduce
# 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