深入理解Blocks,Procs和lambdas
生活随笔
收集整理的這篇文章主要介紹了
深入理解Blocks,Procs和lambdas
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
首發(fā)在:[url]http://blog.enjoyrails.com/?p=125[/url]原文:[url]http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/[/url]
2010.7.17 :在google上搜索一些東西的時(shí)候,發(fā)現(xiàn)有個(gè)網(wǎng)易博客的杭州垃圾轉(zhuǎn)載了這篇文章,說他是垃圾,第一沒有聯(lián)系我,第二沒有添加我文章的鏈接。我又把這篇文章升級(jí)到了Ruby1.9版本的,你繼續(xù)轉(zhuǎn)載啊,垃圾! 再次敬告:轉(zhuǎn)載請(qǐng)注明原文鏈接!
Blocks, Procs和lambdas是Ruby里最重要的方面,同時(shí)也是難點(diǎn)之一。這是因?yàn)镽uby處理閉包(closures)的方式比較特別。更復(fù)雜的是,Ruby有4種使用閉包的方式,它們之間還稍有不同。
First Up, Blocks
大多數(shù)情況下,使用閉包最Ruby的方式就是用Block了。
array = [1,2,3,4]#[1, 2, 3, 4]array.collect! do |n|n ** 2 (求平方)end#[1, 4, 9, 16]puts array.inspect# [1, 4, 9, 16]
so ,what’s going on here?
我們發(fā)送了‘collect!’方法給一個(gè)block代碼的數(shù)組
在collect!方法內(nèi)部給每個(gè)變量作平方(此處為n)
我們現(xiàn)在來實(shí)現(xiàn)自己的collect!方法,我們先建立一個(gè)iterate!方法:
class Arraydef iterate!self.each_with_index do |n,i|self[i] = yield(n)endendendarray = [1,2,3,4]#[1, 2, 3, 4]array.iterate! do |n|n ** 2end#[1, 4, 9, 16]
和屬性不一樣,你并不需要在你的方法內(nèi)部指定block名字,你只需要使用yield關(guān)鍵字就ok了。請(qǐng)注意上例是如何給yield傳參數(shù)n的。整個(gè)過程可以解釋為:
給Array對(duì)象發(fā)送iterate!方法
當(dāng)yield被調(diào)用的時(shí)候,把n(第一次1,第二次4,以此類推)傳給被給的block里
block里有了可用的值,也就是n,那么就平方它。
yield輸出block里返回的值,并且重寫了array里面的值。
使用yield只是使用block代碼的一個(gè)方法而已,這里還有另外一個(gè),那就是作為一個(gè)Proc來調(diào)用它??纯?#xff1a;
class Arraydef iterate!(&code)self.each_with_index do |n,i|self[i] = code.call(n)endendendarr = [1,2,3,4]arr.iterate! do |n|n ** 2end#[1, 4, 9, 16]
和上一個(gè)例子非常相似。然而有兩點(diǎn)不同。第一個(gè)就是iterate!的參數(shù)形式。第二點(diǎn)就是call方法。 輸出結(jié)果是相似的,但是為什么語法不同呢?
def what_am_i(&block)block.classendputs what_am_i{}#Proc這是一個(gè)Proc對(duì)象。block是一個(gè)Proc。<pre lang="RUBY">def what_am_iyield.classendputs what_am_i{ puts "x"}=> xNilClassputs what_am_i{ "x"}# String
這說明,使用yield直接返回的是block表達(dá)式里的值。
那么什么是Proc了?
Procedures, AKA, Procs
我們經(jīng)常需要使用一個(gè)塊在多個(gè)地方,這樣我們就需要不斷的重復(fù)我們的block,那么為了解決這個(gè)問題,做到代碼復(fù)用,我們就可以使用Proc, 其實(shí)block和Proc的唯一區(qū)別就是, block是個(gè)一次性的Proc。
class Arraydef iterate!(code)self.each_with_index do |n, i|self[i] = code.call(n)endendendarr1 = [1,2,3,4]arr2 = [2,3,4,5]square = Proc.new do |n|n ** 2endarr1.iterate!(square)#[1, 4, 9, 16]arr2.iterate!(square)#[4, 9, 16, 25]
你可能會(huì)說,用block不就完了嗎?整個(gè)Proc不是多此一舉。 那么請(qǐng)問,如果我們想給一個(gè)方法傳多個(gè)closures怎么辦?使用block會(huì)很不靈活。 但是用Proc就好多了:
def callbacks(procs)procs[:starting].callputs "Still going"procs[:finishing].callendcallbacks(:starting #Proc.new{puts "Starting"},:finishing #Proc.new{puts "Finishing"})#StartingStill goingFinishing
那么什么時(shí)候用block,什么時(shí)候用Proc呢?Block: 當(dāng)你的方法是被分成多個(gè)小片段,而你想讓你的用戶與這些片段做一些交互。Block: 當(dāng)你需要運(yùn)行多個(gè)原子性表達(dá)式的時(shí)候,比如數(shù)據(jù)庫(kù)遷移(migration)Proc : 當(dāng)你需要重用一個(gè)block的時(shí)候。Proc : 當(dāng)你的方法有一個(gè)或多個(gè)callbacks的時(shí)候。Lambdas匿名函數(shù),在其他語言里也有。Ruby里也可用:
class Arraydef iterate(code)self.each_with_index do |n,i|self[i] = code.call(n)endendendarr = [1,2,3,4]arr.iterate(lambda{|n| n**2 })#[1, 4, 9, 16]
咋看上去,lambda和Proc執(zhí)行的好像是一樣。但是這里有兩個(gè)細(xì)微的不同點(diǎn)。第一個(gè)不同就是lambda檢查參數(shù)的個(gè)數(shù)。
def arguments(code)one, two = 1, 2code.call(one,two)endarguments(Proc.new { |a, b, c| puts "Give me a #{a} and a # and a #{c.class}" })#Give me a 1 and a 2 and a NilClassarguments(lambda { |a, b, c| puts "Give me a #{a} and a # and a #{c.class}" })#ArgumentError: wrong number of arguments (2 for 3)
第二個(gè)不同就是,lambda有一個(gè)‘微小’的返回,什么意思呢?Proc返回會(huì)阻止一個(gè)方法的執(zhí)行,并返回這個(gè)值。lambda返回它們的值,但是方法還會(huì)繼續(xù)執(zhí)行??纯蠢?#xff1a;
def proc_returnProc.new { return "Proc.new"}.callreturn "proc_return method finished"enddef lambda_returnlambda{ return "lambda"}.callreturn "lambda_return method finished"endputs proc_return ? #Proc.newputs lambda_return ?#lambda_return method finished
為什么會(huì)不同?答案就是procedures(過程)和methods(方法)的概念不同。Procs在Ruby里是代碼片段,不是方法。因?yàn)檫@個(gè),Proc return的就是proc_return這個(gè)方法的return,因?yàn)镻roc那段就是那個(gè)方法里的代碼片段。而lambdas的行為像一個(gè)方法,它檢查參數(shù)的個(gè)數(shù),而且不會(huì)覆蓋調(diào)用方法的return。由于這個(gè)原因,你最好把lambdas理解為寫一個(gè)方法的另類方式,只不過是匿名的而已。那么什么時(shí)候該用lamda代替Proc呢?看例子:
def generic_return(code)code.callreturn "generic_return method finished"endputs generic_return(Proc.new{return "Proc.new"})#LocalJumpError: unexpected returnputs generic_return(lambda{return "lambda"})# => generic_return method finished
Ruby語法里,參數(shù)部分不能帶return,然而lambda行為像方法,所以它可以有個(gè)內(nèi)部return。
def generic_return(code)one, two ? ?= 1, 2three, four = code.call(one, two)endputs generic_return(lambda { |x, y| return x + 2, y + 2 })puts generic_return(Proc.new { |x, y| return x + 2, y + 2 })puts generic_return(Proc.new { |x, y| x + 2; y + 2 })puts generic_return(Proc.new { |x, y| [x + 2, y + 2] })# Give me a 3 and a 4#*.rb:11: unexpected return (LocalJumpError)#Give me a 4 and a# ?Give me a 3 and a 4
這個(gè)generic_return方法返回的是兩個(gè)值,使用lambda,一切都很easy,而使用Proc,我們還不得不用數(shù)組指定x,y。
a,b = ?generic_return(lambda { |x, y| return x + 2, y + 2 })#[3, 4]a#3b#4c,d = generic_return(Proc.new { |x, y| x + 2; y + 2 })#[4]c#4d#nile,f = generic_return(Proc.new { |x, y| [x + 2, y + 2] })#[3, 4]e#3f#4
Method Objects如果你有一個(gè)工作完好的方法,你想把它傳到另一個(gè)方法里,作為閉包,也為了使你的代碼DRY,那么你可以使用這個(gè)method方法:
class Arraydef iterate!(code)self.each_with_index do |n,i|self[i] = code.call(n)endendenddef square(n)n ** 2endarr = [1,2,3,4]arr.iterate!(method(:square))#[1, 4, 9, 16]puts method(:square).class# ?Method
這個(gè)方法對(duì)象的行為像是lambda,那么到底。。。:
puts lambda {}.class# ?Proc
呵呵,它是個(gè)Proc?,F(xiàn)在明白R(shí)uby 的這四種閉包方式了吧。block,Proc的行為更像一個(gè)代碼片段,lambdas和method方法的行為更像方法。block,Proc, lambdas都是Proc對(duì)象,而method,是Method對(duì)象。?
2010.7.17 :在google上搜索一些東西的時(shí)候,發(fā)現(xiàn)有個(gè)網(wǎng)易博客的杭州垃圾轉(zhuǎn)載了這篇文章,說他是垃圾,第一沒有聯(lián)系我,第二沒有添加我文章的鏈接。我又把這篇文章升級(jí)到了Ruby1.9版本的,你繼續(xù)轉(zhuǎn)載啊,垃圾! 再次敬告:轉(zhuǎn)載請(qǐng)注明原文鏈接!
Blocks, Procs和lambdas是Ruby里最重要的方面,同時(shí)也是難點(diǎn)之一。這是因?yàn)镽uby處理閉包(closures)的方式比較特別。更復(fù)雜的是,Ruby有4種使用閉包的方式,它們之間還稍有不同。
First Up, Blocks
大多數(shù)情況下,使用閉包最Ruby的方式就是用Block了。
array = [1,2,3,4]#[1, 2, 3, 4]array.collect! do |n|n ** 2 (求平方)end#[1, 4, 9, 16]puts array.inspect# [1, 4, 9, 16]
so ,what’s going on here?
我們發(fā)送了‘collect!’方法給一個(gè)block代碼的數(shù)組
在collect!方法內(nèi)部給每個(gè)變量作平方(此處為n)
我們現(xiàn)在來實(shí)現(xiàn)自己的collect!方法,我們先建立一個(gè)iterate!方法:
class Arraydef iterate!self.each_with_index do |n,i|self[i] = yield(n)endendendarray = [1,2,3,4]#[1, 2, 3, 4]array.iterate! do |n|n ** 2end#[1, 4, 9, 16]
和屬性不一樣,你并不需要在你的方法內(nèi)部指定block名字,你只需要使用yield關(guān)鍵字就ok了。請(qǐng)注意上例是如何給yield傳參數(shù)n的。整個(gè)過程可以解釋為:
給Array對(duì)象發(fā)送iterate!方法
當(dāng)yield被調(diào)用的時(shí)候,把n(第一次1,第二次4,以此類推)傳給被給的block里
block里有了可用的值,也就是n,那么就平方它。
yield輸出block里返回的值,并且重寫了array里面的值。
使用yield只是使用block代碼的一個(gè)方法而已,這里還有另外一個(gè),那就是作為一個(gè)Proc來調(diào)用它??纯?#xff1a;
class Arraydef iterate!(&code)self.each_with_index do |n,i|self[i] = code.call(n)endendendarr = [1,2,3,4]arr.iterate! do |n|n ** 2end#[1, 4, 9, 16]
和上一個(gè)例子非常相似。然而有兩點(diǎn)不同。第一個(gè)就是iterate!的參數(shù)形式。第二點(diǎn)就是call方法。 輸出結(jié)果是相似的,但是為什么語法不同呢?
def what_am_i(&block)block.classendputs what_am_i{}#Proc這是一個(gè)Proc對(duì)象。block是一個(gè)Proc。<pre lang="RUBY">def what_am_iyield.classendputs what_am_i{ puts "x"}=> xNilClassputs what_am_i{ "x"}# String
這說明,使用yield直接返回的是block表達(dá)式里的值。
那么什么是Proc了?
Procedures, AKA, Procs
我們經(jīng)常需要使用一個(gè)塊在多個(gè)地方,這樣我們就需要不斷的重復(fù)我們的block,那么為了解決這個(gè)問題,做到代碼復(fù)用,我們就可以使用Proc, 其實(shí)block和Proc的唯一區(qū)別就是, block是個(gè)一次性的Proc。
class Arraydef iterate!(code)self.each_with_index do |n, i|self[i] = code.call(n)endendendarr1 = [1,2,3,4]arr2 = [2,3,4,5]square = Proc.new do |n|n ** 2endarr1.iterate!(square)#[1, 4, 9, 16]arr2.iterate!(square)#[4, 9, 16, 25]
你可能會(huì)說,用block不就完了嗎?整個(gè)Proc不是多此一舉。 那么請(qǐng)問,如果我們想給一個(gè)方法傳多個(gè)closures怎么辦?使用block會(huì)很不靈活。 但是用Proc就好多了:
def callbacks(procs)procs[:starting].callputs "Still going"procs[:finishing].callendcallbacks(:starting #Proc.new{puts "Starting"},:finishing #Proc.new{puts "Finishing"})#StartingStill goingFinishing
那么什么時(shí)候用block,什么時(shí)候用Proc呢?Block: 當(dāng)你的方法是被分成多個(gè)小片段,而你想讓你的用戶與這些片段做一些交互。Block: 當(dāng)你需要運(yùn)行多個(gè)原子性表達(dá)式的時(shí)候,比如數(shù)據(jù)庫(kù)遷移(migration)Proc : 當(dāng)你需要重用一個(gè)block的時(shí)候。Proc : 當(dāng)你的方法有一個(gè)或多個(gè)callbacks的時(shí)候。Lambdas匿名函數(shù),在其他語言里也有。Ruby里也可用:
class Arraydef iterate(code)self.each_with_index do |n,i|self[i] = code.call(n)endendendarr = [1,2,3,4]arr.iterate(lambda{|n| n**2 })#[1, 4, 9, 16]
咋看上去,lambda和Proc執(zhí)行的好像是一樣。但是這里有兩個(gè)細(xì)微的不同點(diǎn)。第一個(gè)不同就是lambda檢查參數(shù)的個(gè)數(shù)。
def arguments(code)one, two = 1, 2code.call(one,two)endarguments(Proc.new { |a, b, c| puts "Give me a #{a} and a # and a #{c.class}" })#Give me a 1 and a 2 and a NilClassarguments(lambda { |a, b, c| puts "Give me a #{a} and a # and a #{c.class}" })#ArgumentError: wrong number of arguments (2 for 3)
第二個(gè)不同就是,lambda有一個(gè)‘微小’的返回,什么意思呢?Proc返回會(huì)阻止一個(gè)方法的執(zhí)行,并返回這個(gè)值。lambda返回它們的值,但是方法還會(huì)繼續(xù)執(zhí)行??纯蠢?#xff1a;
def proc_returnProc.new { return "Proc.new"}.callreturn "proc_return method finished"enddef lambda_returnlambda{ return "lambda"}.callreturn "lambda_return method finished"endputs proc_return ? #Proc.newputs lambda_return ?#lambda_return method finished
為什么會(huì)不同?答案就是procedures(過程)和methods(方法)的概念不同。Procs在Ruby里是代碼片段,不是方法。因?yàn)檫@個(gè),Proc return的就是proc_return這個(gè)方法的return,因?yàn)镻roc那段就是那個(gè)方法里的代碼片段。而lambdas的行為像一個(gè)方法,它檢查參數(shù)的個(gè)數(shù),而且不會(huì)覆蓋調(diào)用方法的return。由于這個(gè)原因,你最好把lambdas理解為寫一個(gè)方法的另類方式,只不過是匿名的而已。那么什么時(shí)候該用lamda代替Proc呢?看例子:
def generic_return(code)code.callreturn "generic_return method finished"endputs generic_return(Proc.new{return "Proc.new"})#LocalJumpError: unexpected returnputs generic_return(lambda{return "lambda"})# => generic_return method finished
Ruby語法里,參數(shù)部分不能帶return,然而lambda行為像方法,所以它可以有個(gè)內(nèi)部return。
def generic_return(code)one, two ? ?= 1, 2three, four = code.call(one, two)endputs generic_return(lambda { |x, y| return x + 2, y + 2 })puts generic_return(Proc.new { |x, y| return x + 2, y + 2 })puts generic_return(Proc.new { |x, y| x + 2; y + 2 })puts generic_return(Proc.new { |x, y| [x + 2, y + 2] })# Give me a 3 and a 4#*.rb:11: unexpected return (LocalJumpError)#Give me a 4 and a# ?Give me a 3 and a 4
這個(gè)generic_return方法返回的是兩個(gè)值,使用lambda,一切都很easy,而使用Proc,我們還不得不用數(shù)組指定x,y。
a,b = ?generic_return(lambda { |x, y| return x + 2, y + 2 })#[3, 4]a#3b#4c,d = generic_return(Proc.new { |x, y| x + 2; y + 2 })#[4]c#4d#nile,f = generic_return(Proc.new { |x, y| [x + 2, y + 2] })#[3, 4]e#3f#4
Method Objects如果你有一個(gè)工作完好的方法,你想把它傳到另一個(gè)方法里,作為閉包,也為了使你的代碼DRY,那么你可以使用這個(gè)method方法:
class Arraydef iterate!(code)self.each_with_index do |n,i|self[i] = code.call(n)endendenddef square(n)n ** 2endarr = [1,2,3,4]arr.iterate!(method(:square))#[1, 4, 9, 16]puts method(:square).class# ?Method
這個(gè)方法對(duì)象的行為像是lambda,那么到底。。。:
puts lambda {}.class# ?Proc
呵呵,它是個(gè)Proc?,F(xiàn)在明白R(shí)uby 的這四種閉包方式了吧。block,Proc的行為更像一個(gè)代碼片段,lambdas和method方法的行為更像方法。block,Proc, lambdas都是Proc對(duì)象,而method,是Method對(duì)象。?
總結(jié)
以上是生活随笔為你收集整理的深入理解Blocks,Procs和lambdas的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 为什么用lazy启动eclipse的时候
- 下一篇: 12月第四周安全回顾:双节期间微软忙补新