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
The
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.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
The
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..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:
- Procs are full-fledged objects, so they have all the powers and abilities of objects. (Blocks do not.)
- 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.
# 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!
- Create a method,
greeter
, that takes no arguments andyield
s to a block. - Create a Proc,
phrase
, that puts"Hello there!"
. Pass this togreeter
instead of a block. (Don't forget to pass&phrase
instead of justphrase
!)
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 Procsstrings = ["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
lambda { puts "Hello!" }
Is just about the same asProc.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
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!- Create a lambda,
symbol_filter
, that takes one parameter and checks to see if that parameter.is_a? Symbol
. - Create a new variable called
symbols
, and store the result of callingmy_array.select
and passing it your lambda.
symbol_filter = lambda{|x| x.is_a? Symbol}
symbols = my_array.select(&symbol_filter)
16.Quick Review
- 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
. - A proc is a saved block we can use over and over.
- 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