Ruby中的block、proc、lambda區(qū)別總結(jié)
在規(guī)則引擎中,Ruby 的閉包使用特別頻繁,而且有 block,Proc和 lambda 等后幾種形式的用法,很讓人困惑。為了深入理解代碼,再次認(rèn)真學(xué)習(xí)了一下 Ruby 的閉包,特別是 block,proc 和 lambda 幾種用法的異同,這次的周記就和大家分享一下心得。
閉包是 Ruby 相對其它語言特別優(yōu)勢之一,很多語言有閉包,但是唯有 Ruby 把閉包的優(yōu)勢發(fā)揮得淋漓盡致。Ruby 的哲學(xué)之一:同一個問題現(xiàn)實(shí)中有多種解決方法,所以 Ruby 中也有多種解法,所以閉包的使用也有多種方法。
先看一個代碼的例子:
Example 1:
def foo1
yield
end
def foo2(&b)
b.call if b
end
foo1 { puts "foo1 in block" }
foo2 { puts "foo2 in block" }
proc = Proc.new { puts "foo in proc" }
foo1(&proc)
foo2(&proc)
lambda_proc = lambda { puts "foo in lambda" }
foo1(&lambda_proc)
foo2(&lambda_proc)
輸出:
》foo1 in block
》foo2 in block
》foo in proc
》foo in proc
》foo in lambda
》foo in lambda
大家是不是有些困惑,首先是方法 foo1 和 foo2 都能接收閉包,它們這兩種寫法有什么區(qū)別,然后是作為參數(shù)的三種閉包 block,proc和 lambda有什么區(qū)別。
1. yield 和 block call 的區(qū)別
yield 和 block call 兩種都能實(shí)現(xiàn)運(yùn)行閉包,從實(shí)際運(yùn)行效果來說,區(qū)別不大。其區(qū)別主要在于:
1.1 閉包的傳遞和保存
因?yàn)殚]包已經(jīng)傳遞到參數(shù)里,所以可以繼續(xù)傳遞或保存起來,例如:
Exampl 2:
class A
def foo1(&b)
@proc = b
end
def foo2
@proc.call if @proc
end
end
a = A.new
a.foo1 { puts "proc from foo1" }
a.foo2
1.2 性能
yield不是方法調(diào)用,而是 Ruby 的關(guān)鍵字,yield 要比 block call 比快 1 倍左右。
2. block 和 proc, lambda 的區(qū)別
很簡單直接,引入 proc 和 lambda 就是為了復(fù)用,避免重復(fù)定義,例如在 example 1 中,使用 proc 變量存儲閉包,避免重復(fù)定義兩個 block 。
3. proc 和 lambda 的區(qū)別
這大概是最讓人困惑的地方,從 Example 1 的行為上看,他們的效果是一致的,為什么要用兩種不同的表達(dá)方式。
proc = Proc.new { puts "foo in proc" }
lambda_proc = lambda { puts "foo in lambda" }
確實(shí),對于簡單的情況,比如 Example 1的情況,他們的行為是一致的,但是主要在兩個地方有明顯差異:
1.1 參數(shù)檢查
還是例子說話 Example 3:
def foo
x = 100
yield x
end
proc = Proc.new { |a, b| puts "a is #{a.inspect} b is #{b.inspect}" }
foo(&proc)
lambda_proc1 = lambda { |a| puts "a is #{a.inspect}" }
foo(&lambda_proc1)
lambda_proc2 = lambda { |a, b| puts "a is #{a.inspect} b is #{b.inspect}" }
foo(&lambda_proc2)
輸出
》a is 100 b is nil
》a is 100
》ArgumentError: wrong number of arguments (1 for 2)
…
可見,proc 不會對參數(shù)進(jìn)行個數(shù)匹配檢查,而 lambda 會,如果不匹配還會報異常,所以安全起見,建議優(yōu)先用 lambda。
1.2 返回上層
還是例子說話 Example 4:
def foo
f = Proc.new { return "return from foo from inside proc" }
f.call # control leaves foo here
return "return from foo"
end
def bar
f = lambda { return "return from lambda" }
puts f.call # control does not leave bar here
return "return from bar"
end
puts foo
puts bar
輸出
》return from foo from inside proc
》return from lambda
》return from bar
可見,proc 中,return 相當(dāng)于執(zhí)行了閉包環(huán)境里的 return,而 lambda 只是返回call 閉包所在環(huán)境。
總結(jié):閉包是 Ruby 的強(qiáng)大特性,它的幾種表達(dá)方式block,proc 和 lambda有細(xì)微差別,要用好它需要對其深入理解。
- 實(shí)例講解C++編程中l(wèi)ambda表達(dá)式的使用
- 結(jié)合C++11新特性來學(xué)習(xí)C++中l(wèi)ambda表達(dá)式的用法
- C++中的Lambda表達(dá)式詳解
- C++實(shí)現(xiàn)的一個可以寫遞歸lambda的Y函數(shù)
- Ruby中使用Block、Proc、lambda實(shí)現(xiàn)閉包
- python中l(wèi)ambda函數(shù) list comprehension 和 zip函數(shù)使用指南
- Python中的特殊語法:filter、map、reduce、lambda介紹
- Ruby中proc和lambda的兩個區(qū)別
- 淺析C++11新特性的Lambda表達(dá)式
相關(guān)文章
關(guān)于Ruby on Rails視圖編寫的一些建議
這篇文章主要介紹了關(guān)于Ruby on Rails視圖編寫的一些建議,有助于團(tuán)隊協(xié)作時對代碼的調(diào)試工作,需要的朋友可以參考下2015-08-08PHP實(shí)現(xiàn)的一個保存遠(yuǎn)程文件到本地的函數(shù)分享
這篇文章主要介紹了PHP實(shí)現(xiàn)的一個保存遠(yuǎn)程文件到本地的函數(shù)分享,本文直接給出實(shí)現(xiàn)代碼和使用方法,需要的朋友可以參考下2014-11-11Monkey Patch猴子補(bǔ)丁編程方式及其在Ruby中的運(yùn)用
Monkey Patch是指在程序運(yùn)行時追加代碼段,一般被認(rèn)為是解釋型語言的特長,下面我們就來看一下Monkey Patch猴子補(bǔ)丁編程方式及其在Ruby中的運(yùn)用2016-05-05Ruby使用REXML庫來解析xml格式數(shù)據(jù)的方法
這篇文章主要介紹了Ruby使用REXML庫來解析xml格式數(shù)據(jù)的方法,文章最后提及了REXML庫的使用相關(guān)安全問題可以注意一下,需要的朋友可以參考下2016-04-04