开发者

How do you use Ruby blocks to conditionally execute something?

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?

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜