本文主要介紹了ruby開發(fā)中的比較容易混淆的幾個概念空凸,并以簡單示例的形式展現(xiàn)他們之間的區(qū)別,明白了這些區(qū)別之后馋艺,在今后的開發(fā)中可以準確并優(yōu)雅的使用它們召娜。
當大家在百度中搜索“block proc lambda”的時候掠抬,會出來很多關(guān)于這幾個概念之間區(qū)別的介紹,既然搜索結(jié)果中已經(jīng)有了這些介紹,那為什么還要寫這篇文章?
相信看過百度搜索結(jié)果中排名靠前的幾篇文章的同學私蕾,都會發(fā)現(xiàn)其實這些文章并沒有很好的說明他們之間區(qū)別是什么,大多只是介紹各自的用法胡桃,加上些許的區(qū)別踩叭,即使個別介紹了一些區(qū)別,也不夠系統(tǒng)翠胰,不夠深入懊纳。
正是基于上述原因,才醞釀了本文亡容。本文以簡單示例的方式,詳細的介紹了它們之間的區(qū)別冤今。相信您閱讀完本文闺兢,一定會豁然開朗,并在今后的開發(fā)中準確并優(yōu)雅的使用它們。
由于時間屋谭,個人能力水平等有限脚囊,本文難免有錯誤或缺失之處,歡迎不吝指出桐磁。
block & proc
在介紹它們的區(qū)別之前悔耘,我們先來看一段有關(guān)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
以上介紹了兩種在函數(shù)中使用block的方式,第一種是通過yield來使用block我擂,另外一種則是通過block.call來使用block衬以。
proc全稱為procedure,中文翻譯為過程校摩,步驟看峻。關(guān)于block和proc的區(qū)別,我們先來看一個簡單的示例衙吩。
def what_am_i(&block)
block.class
end
puts?what_am_i?{}#?=<?Proc
定義一個函數(shù)what_am_i并接受一個block互妓,執(zhí)行體中打印了block的類型,從執(zhí)行的結(jié)果我們看到block的類型為proc,即說明block為proc的一種坤塞。
block和proc之間的區(qū)別主要有兩個:一是proc可以重復使用冯勉,如果某個block將在多個地方被用到,則我們可以將其定義為proc摹芙。另外一個區(qū)別就是當某個函數(shù)需要執(zhí)行多個閉包的時候灼狰,此時我們就無法使用block而只有使用proc或其他的閉包。
示例如下瘫辩,在執(zhí)行某個具體的操作的前后伏嗜,調(diào)用了我們自己的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
關(guān)于proc和lambda的區(qū)別伐厌,我們先來看一個簡單的例子承绸。
def args(code)
code.call'one','two'
end
args Proc.new{?|a,b|?puts?a,b}
args?lambda?{|a,b|?puts?a,b}
上述示例,第一眼看去發(fā)覺lambda和proc之間沒什么區(qū)別或者很難發(fā)現(xiàn)它們的區(qū)別是什么挣轨。
接下來军熏,我們對上述示例做一下簡單的修改,我們將之前的接受兩個參數(shù)修改為三個卷扮,看看會發(fā)生什么情況荡澎。
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)
運行修改后的示例,發(fā)現(xiàn)lambda閉包出錯了晤锹,出錯的信息為摩幔,錯誤的參數(shù)個數(shù)。
至此鞭铆,我們很快就發(fā)現(xiàn)了它們之間的一個區(qū)別是或衡,lambda會檢查參數(shù)的個數(shù),而proc則不會,proc默認將缺失的參數(shù)視為nil封断,并繼續(xù)執(zhí)行代碼斯辰,這是為什么呢?在本節(jié)的最后坡疼,我們會道出這其中的緣由彬呻。
在了解到它們的第一個區(qū)別之后,接下來我們再來看一個示例柄瑰。
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
這個示例非常的簡單闸氮,我們定義了兩個函數(shù)proc_return以及l(fā)ambda_return。這兩個函數(shù)一個用proc實現(xiàn)狱意,另外一個用lambda實現(xiàn)湖苞,執(zhí)行體都只有一行代碼,就是返回一段字符串详囤。
執(zhí)行的結(jié)果出乎我們的意料财骨,proc并未執(zhí)行return之后的代碼,而lambda執(zhí)行了return之后的代碼藏姐。
綜上所述隆箩,我們得出了proc和lambda的兩個重要區(qū)別,一是lambda會進行參數(shù)個數(shù)的檢查而proc則不會羔杨,另外lambda會執(zhí)行return之后的代碼而proc則不會捌臊。
為什么會出現(xiàn)上述情況,本質(zhì)的原因在于兜材,proc只是一段嵌入的代碼片段而lambda則是匿名函數(shù)理澎,正因為是匿名函數(shù),所以會檢查函數(shù)調(diào)用參數(shù)曙寡,并在函數(shù)調(diào)用結(jié)束之后糠爬,繼續(xù)執(zhí)行后面的代碼,而proc由于是嵌入的一段代碼片段举庶,在執(zhí)行完return語句后执隧,就已經(jīng)返回,所以不再執(zhí)行之后的代碼户侥。
lambda & method object
def greeting
puts'hello,?method?object'
end
method(:greeting).call
lambda {puts'hello,?lambda'}.call
lambda和method object的用法基本一致镀琉,其唯一的區(qū)別在于lambda為匿名函數(shù),而method object為命名函數(shù)蕊唐。
總結(jié)
關(guān)于block,proc,lambda, method object這四者之間的區(qū)別可總結(jié)為以下:
block和proc本質(zhì)上是一段嵌入的代碼塊屋摔,并非函數(shù)。而lambda和method object都是函數(shù)替梨,只不過lambda是匿名函數(shù)钓试,而method object為命名函數(shù)署尤。
從本質(zhì)上理解了它們的區(qū)別,我們在今后的開發(fā)中就會正確且優(yōu)雅的運用它們亚侠。
引用
[1]http://www.reactive.io/tips/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/
相關(guān)代碼
來自:http://my.oschina.net/gschen/blog/325546