I recently purchased the book Seven Languages in Seven Weeks and have been reading through the chapter on Ruby. In the section which introduces blocks (page 40), a code sample is given which illustrates the use of blocks for the purpose of conditionally executing something:
in_case_of_emergency do
use_credit_card
panic
end
def in_case_of_emergency
yield if emergency?
end
This code doesn't make much sense to me, and the book doesn't provide much of an explanation. I was wondering if one of you Ruby gurus would mind helping me get my head around this one.
How can you have both a block and a function with the same name? How would you define "emergency?" I can't even create the block in IRB without it complaining:
NoMethodError: undefined method `in_case_of_emergency' for main:Object
from (irb):1
from :0
And how would you invoke this code开发者_运维问答 to demonstrate how it works? Thanks!
First off: the two are in the wrong order. You need to define in_case_of_emergency
first.
Second: You don't name blocks; therefore, it is incorrect that there are two things named in_case_of_emergency
. One is a function definition, while the second is the function invocation of that same function.
So, step-by-step:
def emergency?
return rand(2) == 0
end
Let's say you have this function that returns true
half the time, and false
half the time, randomly. (Boy, that's a lot of emergencies!) Then:
def in_case_of_emergency
yield if emergency?
end
This defines a function named in_case_of_emergency
. When called, it executes yield if emergency?
, which is a statement yield
modified by the conditional if
. This is syntactic sugar for
if emergency?()
yield
end
Note that Ruby does not require brackets for function invocation, thus we can drop them; and if there is only one statement inside an if
, you can write it on the same line as above (obviating the need for an end
).
Next, we have a function invocation:
in_case_of_emergency do
use_credit_card
panic
end
This calls the function we just defined, in_case_of_emergency
, passing it a block to execute. These are the two statements (use_credit_card
, panic
) that would be executed by yield
- but only if the emergency?
evaluates to true
.
Does this make more sense now?
that's simple, that is the method:
def in_case_of_emergency
yield if emergency?
end
and that is the call to your method:
in_case_of_emergency do
use_credit_card
panic
end
wheres
do
use_credit_card
panic
end
is an argument. Ruby methods can implicitly take blocks as arguments. What happens is that
yield
will execute the block you provided. In your case yield if emergency?
means "execute provided block if conditions are met".
Adding to other answers, it may be easier to understand blocks if you drop the yield
keyword and treat blocks in methods like the Procs they actually are.
# for testing purposes, let's always have `emergency?` return `true`
def emergency?
true
end
def in_case_of_emergency(&block)
block.call if emergency?
end
in_case_of_emergency do
puts "AHH! Emergency! Help!", "Seriously, I'm freaking out!"
end
See how much easier it is to realize that the block is actually an argument?
精彩评论