Wednesday, April 13, 2016

Ruby_Lesson_Blocks, Procs, and Lambdas

Blocks:A Reintroduction
1.You Know This!
We said earlier that a block is like a nameless method, but that's not quite true. (We'll get to actual nameless methods, called lambdas)
A Ruby block is just a bit of code that can be executed. Block syntax uses either do..end or curly braces ({})
2.Collect 'Em All
There are a bunch of really useful Ruby methods that take blocks. One we haven't covered yet is collect.
The collect method takes a block and applies the expression in the block to every element in an array. Check it out:
my_nums = [1, 2, 3]
my_nums.collect { |num| num ** 2 }
# ==> [1, 4, 9]
If we look at the value of my_nums, though, we'll see it hasn't changed:
my_nums
# ==> [1, 2, 3]
This is because .collect returns a copy of my_nums, but doesn't change (or mutate) the original my_nums array. If we want to do that, we can use .collect! with an exclamation point:
my_nums.collect! { |num| num ** 2 }
# ==> [1, 4, 9]
my_nums
# ==> [1, 4, 9]
Recall that the ! in Ruby means "this method could do something dangerous or unexpected!" In this case, it mutates the original array instead of creating a new one.
3.Learning to Yield
Why do some methods accept a block and others don't? It's because methods that accept blocks have a way of transferring control from the calling method to the block and back again. We can build this into the methods we define by using the yield keyword.
def block_test
  puts "We're in the method!"
  puts "Yielding to the block..."
  yield
  puts "We're back in the method!"
end
block_test { puts ">>> We're in the block!" }

4.Yielding With Parameters
def yield_name(name)
  puts "In the method! Let's yield."
  yield("Kim")
  puts "In between the yields!"
  yield(name)
  puts "Block complete! Back in the method."
end
yield_name("Eric") { |n| puts "My name is #{n}." }

Procs: Savable Blocks
6.Keeping Your Code DRY
Blocks are not objects, and this is one of the very few exceptions to the "everything is an object" rule in Ruby.Because of this, blocks can't be saved to variables and don't have all the powers and abilities of a real object.
You can think of a proc as a "saved" block: just like you can give a bit of code a name and turn it into a method, you can name a block and turn it into a proc. Procs are great for keeping your code DRY, which stands for Don't Repeat Yourself. With blocks, you have to write your code out each time you need it; with a proc, you write your code once and can use it many times!
7.Proc Syntax
Procs are easy to define! You just call Proc.new and pass in the block you want to save. Here's how we'd create a proc called cube that cubes a number (raises it to the third power):
cube = Proc.new { |x| x ** 3 }
We can then pass the proc to a method that would otherwise take a block, and we don't have to rewrite the block over and over!
[1, 2, 3].collect!(&cube)
# ==> [1, 8, 27]
[4, 5, 6].map!(&cube)
# ==> [64, 125, 216]
(The .collect! and .map! methods do the exact same thing.)
The & is used to convert the cube proc into a block (since .collect! and .map! normally take a block). We'll do this any time we pass a proc to a method that expects a block.
7.1.The .floor method rounds a float (a number with a decimal) down to the nearest integer. Write a proc called round_down that will do this rounding (we've added the code to pass it to floats.collect).
floats = [1.2, 3.45, 0.91, 7.727, 11.42, 482.911]
round_down = Proc.new {|x| x.floor}
ints = floats.collect(&round_down)

8.Why Procs?
Why bother saving our blocks as procs? There are two main advantages:
  1. Procs are full-fledged objects, so they have all the powers and abilities of objects. (Blocks do not.)
  2. Unlike blocks, procs can be called over and over without rewriting them. This prevents you from having to retype the contents of your block every time you need to execute a particular bit of code.
# Here at the amusement park, you have to be four feet tall
# or taller to ride the roller coaster. Let's use .select on
# each group to get only the ones four feet tall or taller.

group_1 = [4.1, 5.5, 3.2, 3.3, 6.1, 3.9, 4.7]
group_2 = [7.0, 3.8, 6.2, 6.1, 4.4, 4.9, 3.0]
group_3 = [5.5, 5.1, 3.9, 4.3, 4.9, 3.2, 3.2]

over_4_feet = Proc.new {|x| x>=4}

can_ride_1 = group_1.select(&over_4_feet)
can_ride_2 = group_2.select(&over_4_feet)
can_ride_3 = group_3.select(&over_4_feet)

9.Create Your Own!
  1. Create a method, greeter, that takes no arguments and yields to a block.
  2. Create a Proc, phrase, that puts "Hello there!". Pass this to greeter instead of a block. (Don't forget to pass &phrase instead of just phrase!)
def greeter
    yield
end
phrase = Proc.new{ puts "Hello there!"}
greeter(&phrase)

10.Call Me Maybe
Unlike blocks, we can call procs directly by using Ruby's .call method. Check it out!
test = Proc.new { # does something }
test.call
# does that something!
11.Symbols, Meet Procs
strings = ["1", "2", "3"]
nums = strings.map(&:to_i)
# ==> [1, 2, 3]
By mapping &:to_i over every element of strings, we turned each string into an integer!
numbers_array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
strings_array = numbers_array.collect(&:to_s)

Lamdas
12.The Ruby Lambda
Like procs, lambdas are objects. The similarities don't stop there: with the exception of a bit of syntax and a few behavioral quirks, lambdas are identical to procs.
lambda { puts "Hello!" }
Is just about the same as
Proc.new { puts "Hello!" }
def lambda_demo(a_lambda)
  puts "I'm the method!"
  a_lambda.call
end
lambda_demo(lambda { puts "I'm the lambda!" })

13.Lambda Syntax
Lambdas are defined using the following syntax:
lambda { |param| block }
Lambdas are useful in the same situations in which you'd use a proc. strings = ["leonardo", "donatello", "raphael", "michaelangelo"]
symbolize = lambda{|x| x.to_sym}
symbols = strings.collect(&symbolize)

14.Lambdas vs. Procs
First, a lambda checks the number of arguments passed to it, while a proc does not. This means that a lambda will throw an error if you pass it the wrong number of arguments, whereas a proc will ignore unexpected arguments and assign nil to any that are missing.
Second, when a lambda returns, it passes control back to the calling method; when a proc returns, it does so immediately, without going back to the calling method.
def batman_ironman_proc
  victor = Proc.new { return "Batman will win!" }
  victor.call
  "Iron Man will win!"
end
puts batman_ironman_proc
def batman_ironman_lambda
  victor = lambda { return "Batman will win!" }
  victor.call
  "Iron Man will win!"
end
puts batman_ironman_lambda

Batman will win!
Iron Man will win!
15.Now You Try!
Let's riff on it with a lambda that checks to see if each element in an array is a symbol. We can do this checking with the .is_a? method, which returns true if an object is the type of object named and false otherwise:
:hello.is_a? Symbol
# ==> true
The word Symbol has to be capitalized when you're doing an .is_a? check!
  1. Create a lambda, symbol_filter, that takes one parameter and checks to see if that parameter .is_a? Symbol.
  2. Create a new variable called symbols, and store the result of calling my_array.select and passing it your lambda.
my_array = ["raindrops", :kettles, "whiskers", :mittens, :packages]
symbol_filter = lambda{|x| x.is_a? Symbol}
symbols = my_array.select(&symbol_filter)

16.Quick Review
  1. A block is just a bit of code between do..end or {}. It's not an object on its own, but it can be passed to methods like .each or .select.
  2. A proc is a saved block we can use over and over.
  3. A lambda is just like a proc, only it cares about the number of arguments it gets and it returns to its calling method rather than returning immediately.










No comments :

Post a Comment