淺談Ruby中的block, proc, lambda, method object的區別
本文主要介紹了ruby開發中的比較容易混淆的幾個概念,并以簡單示例的形式展現他們之間的區別,明白了這些區別之后,在今后的開發中可以準確并優雅的使用它們。
當大家在百度中搜索“block proc lambda”的時候,會出來很多關于這幾個概念之間區別的介紹,既然搜索結果中已經有了這些介紹,那為什么還要寫這篇文章?
相信看過百度搜索結果中排名靠前的幾篇文章的同學,都會發現其實這些文章并沒有很好的說明他們之間區別是什么,大多只是介紹各自的用法,加上些許的區別,即使個別介紹了一些區別,也不夠系統,不夠深入。
正是基于上述原因,才醞釀了本文。本文以簡單示例的方式,詳細的介紹了它們之間的區別。相信您閱讀完本文,一定會豁然開朗,并在今后的開發中準確并優雅的使用它們。
由于時間,個人能力水平等有限,本文難免有錯誤或缺失之處,歡迎不吝指出。
block & proc
在介紹它們的區別之前,我們先來看一段有關block的簡單使用方法:
def use_yield yield end use_yield do puts 'use yield' end def use_block_call(&block) block.call end use_block do puts 'use block call' end
以上介紹了兩種在函數中使用block的方式,第一種是通過yield來使用block,另外一種則是通過block.call來使用block。
proc全稱為procedure,中文翻譯為過程,步驟。關于block和proc的區別,我們先來看一個簡單的示例。
def what_am_i(&block)
block.class
end
puts what_am_i {} # =< Proc
定義一個函數what_am_i并接受一個block,執行體中打印了block的類型,從執行的結果我們看到block的類型為proc,即說明block為proc的一種。
block和proc之間的區別主要有兩個:一是proc可以重復使用,如果某個block將在多個地方被用到,則我們可以將其定義為proc。另外一個區別就是當某個函數需要執行多個閉包的時候,此時我們就無法使用block而只有使用proc或其他的閉包。
示例如下,在執行某個具體的操作的前后,調用了我們自己的proc。
def action(code)
code[:before].call
puts 'processing...'
code[:after].call
end
action :before => Proc.new {puts 'before action'},
:after => Proc.new {puts 'after action'}
proc & lambda
關于proc和lambda的區別,我們先來看一個簡單的例子。
def args(code)
code.call 'one','two'
end
args Proc.new { |a,b| puts a,b}
args lambda {|a,b| puts a,b}
上述示例,第一眼看去發覺lambda和proc之間沒什么區別或者很難發現它們的區別是什么。
接下來,我們對上述示例做一下簡單的修改,我們將之前的接受兩個參數修改為三個,看看會發生什么情況。
def args(code)
code.call 'one','two'
end
args Proc.new { |a,b,c| puts a,b,c}
args lambda {|a,b,c| puts a,b,c} # =< wrong number of arguments (2 for 3) (ArgumentError)
運行修改后的示例,發現lambda閉包出錯了,出錯的信息為,錯誤的參數個數。
至此,我們很快就發現了它們之間的一個區別是,lambda會檢查參數的個數,而proc則不會,proc默認將缺失的參數視為nil,并繼續執行代碼,這是為什么呢?在本節的最后,我們會道出這其中的緣由。
在了解到它們的第一個區別之后,接下來我們再來看一個示例。
def proc_return?
Proc.new {return 'Proc.new'}.call
puts 'proc_return method finished'
end
def lambda_return
lambda { return 'lambda'}.call
puts 'lambda_return method finished'
end
puts proc_return # =< Proc.new
puts lambda_return # =< lambda_return method finished
這個示例非常的簡單,我們定義了兩個函數proc_return以及lambda_return。這兩個函數一個用proc實現,另外一個用lambda實現,執行體都只有一行代碼,就是返回一段字符串。
執行的結果出乎我們的意料,proc并未執行return之后的代碼,而lambda執行了return之后的代碼。
綜上所述,我們得出了proc和lambda的兩個重要區別,一是lambda會進行參數個數的檢查而proc則不會,另外lambda會執行return之后的代碼而proc則不會。
為什么會出現上述情況,本質的原因在于,proc只是一段嵌入的代碼片段而lambda則是匿名函數,正因為是匿名函數,所以會檢查函數調用參數,并在函數調用結束之后,繼續執行后面的代碼,而proc由于是嵌入的一段代碼片段,在執行完return語句后,就已經返回,所以不再執行之后的代碼。
lambda & method object
def greeting
puts 'hello, method object'
end
method(:greeting).call
lambda{puts 'hello, lambda'}.call
lambda和method object的用法基本一致,其唯一的區別在于lambda為匿名函數,而method object為命名函數。
總結
關于block,proc,lambda, method object這四者之間的區別可總結為以下:
block和proc本質上是一段嵌入的代碼塊,并非函數。而lambda和method object都是函數,只不過lambda是匿名函數,而method object為命名函數。
從本質上理解了它們的區別,我們在今后的開發中就會正確且優雅的運用它們。
引用
[1] http://www.reactive.io/tips/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/
相關代碼
來自:http://my.oschina.net/gschen/blog/325546