Blocks, Procs, and Lambdas are powerful aspects of the Ruby language. Although they are similar in many ways, depending on your objective, it might make sense to choose one over the others. So, what are each of them and how do they differ?
Blocks & Procs
Lets say that we have an array of numbers that we want to adjust in several ways:nums = [1,2,3,4,5,6,7,8,9,10]
We might add 2 to every number by typing something like this:p nums.map! {|num| num + 2}
[3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
What did we do there, exactly? Well, the map! method knows to iterate through each object within the nums array. For each object in the array, the map! method executes the instructions passed to it in the block of code to the right (within the { }). This block of code takes each object in the array as an input, assigns it the temporary variable name 'num', and then changes that num by adding 2.What's important to take away here, though, is that the instructions we passed to the map! method were in a block. A ruby block is set of code that can be executed, and is one of the few items in ruby that is not considered an object.
But what if our plan was to increment this array several times? We might get tired of continually typing
p nums.map! {|num| num + 2}
everytime we wanted to increment the array. Instead, we could save those instructions as a Proc that we can access by name. A proc is essentially just a block of code that is saved with a variable name to be accessed later. We could establish a Proc that will increment our array like this:add_2 = Proc.new {|num| num + 2}
And now if we want to increment the array 3 times, we can just write:
p nums.map!(&add_2)
p nums.map!(&add_2)
p nums.map!(&add_2)
[3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
[7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
temp = Proc.new {|num| num > 12 ? "hot" : num.between?(9,12) ? "warm" : "cold"}
p nums.map(&temp)
["cold", "cold", "warm", "warm", "warm", "warm", "hot", "hot", "hot", "hot"]
p nums.map!(&add_2)
p nums.map(&temp)
[9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
["warm", "warm", "warm", "warm", "hot", "hot", "hot", "hot", "hot", "hot"]
As you can see, the use of Procs not only saves you time, but also prevents unnecessary repetition and can help make your code even easier to read.
Procs vs. Lambdas
A lambda is another way of storing a block of code, and only differs from Procs slightly. The two major differences between a lambda and a Proc are that:- A lambda checks that the amount of arguments passed into it is correct. For example, if a lambda expects 3 arguments to be passed in, and only 2 are, the code will not execute and will return an error. If you pass in fewer arguments than are available for a Proc, a value of nil will automatically be applied to any unfulfilled arguments. Any unexpected arguments will be ignored.
- A lambda will 'return' back to a method that it is built into, whereas a Proc will break the calling method. For example, if you access a lambda from within a method, and that lambda includes a 'return' function, it will break you back into the method it was called from, and any remaining code within the method will be run. If you accessed a Proc from a method, and 'returned' from that Proc, you will break out of the method and the remaining code within that method will not be run.
def greet(person)
--greeting = lambda {return "Hey there #{person}!! So great to talk to you!"}
--puts greeting.call
--"How are things going?"
end
puts greet("craig")
Hey there craig!! So great to talk to you!
How are things going?
def greet(person)
--greeting = Proc.new {return "Hey there #{person}!! So great to talk to you!"}
--puts greeting.call
--"How are things going?"
end
puts greet("craig")
Hey there craig!! So great to talk to you!
Notice how only the string within the Proc was returned, and not the second one like when we used a lambda? That's because when returning out of a Proc, the method from which we accessed that Proc is broken out of. Returning out of a lambda takes us right back to where we were within the method.As you can see, blocks, Procs, and lambdas are very powerful. Which one you use at which point in time will be dictated by the functionality you are trying to build, and whether you'll want to re-use that functionality several times over.
Thanks for reading!
Copyright: Gary Hammell 2014