1.Block:
Ruby中的塊就是由多行程式碼群組成的一個代碼塊,通常可以把它認為是一個匿名方法,常用來迭代一個數組或範圍(如each, times方法);文法格式如下:
代碼如下 |
複製代碼 |
{ //code } OR do //code end |
塊變數:當建立一個塊時,在兩個豎線之間(如: | i |)被稱作塊變數,作用和一個正常方法的參數一樣;如:5.each{| x | puts x }
2.Block and Array
Block常用來迭代數組,上面也提到過,所以數組中也定義了很多方法接受塊參數;常用的有:
collect:該方法把數組的每一個元素傳給塊並返回一個新的包括所有元素的數組,原數組的值不變。如果使用collect!方法剛會修改原數組的值;
each:each方法和collect方法有點類似,會把數組的每個元素的值傳遞給塊,但和collect不同的是,each方法不會建立一個新的包含傳回值的數組;沒有each!方法;
例:
代碼如下 |
複製代碼 |
a = [1,2,3] b = a.collect{|x| x*2} puts("-a-") puts a puts("-b-") puts b c = a.collect!{|x| x*2} puts("-a-") puts a --------------result------------------ -a- 1 3 -b- 2 6 -a- 2 6 |
另外,我們怎麼迭代一個字串中的每一個字元?我們首先要做的是用split方法分割字串,然後再進行迭代;
例:
代碼如下 |
複製代碼 |
a = "hello".each{|x| puts x} a = "hello".split (//).each{|x| puts x} -------------------result------------------------------- hello h e l l o |
Block在Ruby在有些特別,因為block變不是對象,這與ruby中”一切皆對象”不符合;每一個對象都建立於一個類,我們可以使用class方法來找到某個對象屬於哪個類;
例:
代碼如下 |
複製代碼 |
puts( { 1=>2 }.class ) #Hash puts( {|x| puts(x) }.class #error |
3.Proc and Lambda
雖然block預設並不是對象,它們可以”變成”對象。有三種方法用於從block建立對象並把它們分配給變數,格式如下:
代碼如下 |
複製代碼 |
a = Proc.new{ |x| puts x } b = lambda{ |x| puts x } c = proc{ |x| puts x } |
讓我們分別來看一下這三種建立方法;首先,我們可以用Proc.new來建立一個對象並且把一個block作為參數傳遞給它; 然後,我們可以使用Proc類的call(可以傳遞一個或多個參數給call方法,這些參數會傳遞到block裡,參數個數取決於block中參數的個數)方法來執行block中的代碼;
我們也可以使用其它兩種方法建立一個Proc對象,三種方法都差不多;唯一的區別就是用Proc.new建立的對象不會檢查參數的數量,其它兩種方法會check;
例:
代碼如下 |
複製代碼 |
a = Proc.new{|x,y,z| x=y*z; puts x} a.call(10,20,30,40) #=>600 b = proc{|x,y,z| x=y*z; puts x} b.call(10,20,30,40) #=>error c = lambda{|x,y,z| x=y*z; puts x} c.call(10,20,30,40) #=>error |
4.Yield
在Ruby中可以把block當作參數傳遞給方法,方法裡面再使用yield(可以傳遞參數)關鍵字調用代碼塊;
例1:不帶參數
代碼如下 |
複製代碼 |
class Person def go() yield end end p = Person.new p.go {puts("hello world")} |
Note:我們只是簡單的把block放在我們想傳遞進去的方法的右邊,方法接收block,當執行到yield時就會調用block的代碼;
例2:帶參數
代碼如下 |
複製代碼 |
class Person def go(spead) yield(spead) end end p = Person.new p.go("ten miles per hour!") {|x| x.capitalize! puts x} |
Note:
1.在些例中,go方法帶有一個參數,spead,並且把這個參數傳遞到被yield執行的block裡面;當調用go方法時,我傳遞了一個參數(“ten miles per hour!”),當執行到yield語句時,會傳遞給block參數;
2.使用yield關鍵字調用代碼塊時,如果傳入的參數個數少於代碼塊中定義的參數個數,那麼沒有傳遞的參數會自動轉為nil。反之,那麼最後一個參數為一個數組,該數組包含了剩餘的傳遞參數;
5.傳遞命名的Proc對象
在Ruby中定義方法時,如果在方法的最後一個形參前增加一個”&”符號,那麼Ruby會把這個形參作為一個Proc對象處理(例2);而Proc對象實際上就是一個代碼塊的封裝體,因此在調用方法時需要傳遞一個block作為參數;
例1:
代碼如下 |
複製代碼 |
def abc( a, b, c ) a.call #<= call block a b.call #<= call block b c.call #<= call block c yield #<= yield unnamed block: { puts "four" } end abc(a, b, c ){ puts "four" } |
例2:
代碼如下 |
複製代碼 |
def test( &a ) a.call #<= block &d yield yield #<= also block &d end test{ puts "hello" } #法一,傳遞一個塊 a = proc{puts "world"} test(&a) #法二,傳遞一個Proc對象 |
6.程式優先順序
在傳遞一個block時,使用{}傳遞的block比使用do…end的優先順序要高;為了避免引起歧義,最好使用大括弧將參數括起來;
1. foo bar do…end:傳遞到foo方法裡面,bar會作為一個參數傳遞給foo
2. foo bar {…}:block會傳遞到bar裡面,返回的值會作為參數傳遞給方法foo
例:
代碼如下 |
複製代碼 |
def foo( b ) puts("---in foo---") a = 'foo' if block_given? puts( "(Block passed to foo)" ) yield( a ) else puts( "(no block passed to foo)" ) end puts( "in foo, arg b = #{b}" ) return "returned by " << a end def bar puts("---in bar---") a = 'bar' if block_given? puts( "(Block passed to bar)" ) yield( a ) else puts( "(no block passed to bar)" ) end return "returned by " << a end # ========== Syntax "A" - do..end ======= puts( '--- (A) do block ---' ) # calls foo with block foo bar do |s| puts( s ) end # the above is equivalent to # foo( bar ) do |s| puts( s ) end # or # foo( bar ) { |s| puts(s) } puts # ========== Syntax "B" - {} ======= puts( '--- (B) curly braces block ---' ) # calls bar with block foo bar{ |s| puts(s) } ------------------------result---------------------------------- --- (A) do block --- ---in bar--- (no block passed to bar) ---in foo--- (Block passed to foo) foo in foo, arg b = returned by bar --- (B) curly braces block --- ---in bar--- (Block passed to bar) bar ---in foo--- (no block passed to foo) in foo, arg b = returned by bar |
Note:我們可以使用block_given?方法來判定一個方法是否接收了一個block;