ruby的blocks和closure特性明顯有別于其它的語言,其closure本身是real closure,所綁定的context是共享的而非copy,其設計思路和lisp的相同;blocks本身則可以用于實現closure。二者的關系如下所述 (
來源)
Yukihiro Matsumoto: You can reconvert a closure back into a block, so a closure can be used anywhere a block can be used. Often, closures are used to store the status of a block into an instance variable, because once you convert a block into a closure, it is an object that can by referenced by a variable. And of course closures can be used like they are used in other languages, such as passing around the object to customize behavior of methods. If you want to pass some code to customize a method, you can of course just pass a block. But if you want to pass the same code to more than two methods -- this is a very rare case, but if you really want to do that -- you can convert the block into a closure, and pass that same closure object to multiple methods.
def thrice
yield
yield
yield
end
x=1
thrice {x+=2}
def six_times(&block)
thrice(&block)
thrice(&block)
end
x = 4
six_times { x += 10 }
- &block傳入,保存block為變量,然后調用block.call
def save_for_later(&b)
@saved = b # Note: no ampersand! This turns a block into a closure of sorts.
end
save_for_later { puts "Hello!" }
puts "Deferred execution of a block:"
@saved.call
@saved.call
這里的saved保存為main對象的一個成員,后邊實現延遲調用。
@saved_proc_new = Proc.new { puts "I'm declared with Proc.new." }
@saved_proc = proc { puts "I'm declared with proc." }
@saved_proc_new.call
@saved_proc.call
@saved_lambda = lambda { puts "I'm declared with lambda." }
@saved_lambda.call
def some_method
puts "I'm declared as a method."
end
@method_as_closure = method(:some_method)
當對應的block里邊包含return的時候,上述7中方式有些許的不同:
- lambda/method表現出真正的closure行為,僅僅返回closure本身;外部調用控制流不受影響,繼續yield或者call的下一語句執行
- 其它幾種會跳出外部調用者的控制流,即return出調用者,yield/call之后的也不會再執行,直接跳出到最近的end外
對于調用點的參數檢查,呈現如下行為:
- lambda/method嚴格校驗參數的個數,如果不匹配回拋出異常
- 其它幾個不檢查參數個數
lambda/method方式呈現完備的closure行為,return之后繼續下一流程,對于實際傳入參數個數會在調用點檢查;proc/blocks方式在return的時候直接返回了外部的函數或者block,對于傳入的參數個數也沒有執行檢查。
參考:
http://innig.net/software/ruby/closures-in-ruby
以上結論驗證于最新的ruby1.9.3,和鏈接中的結論有些許不同;ruby1.8中的proc函數可能有不同的行為。