Block的內(nèi)部調(diào)用過程:
調(diào)用method three_times的時候,遇見yield,就立即調(diào)用Block,block運行完畢,馬上返回yield的下一句
method_name(parameter,parameter){block} 這里,解釋器遇見method_name(parameter,parameter)就進入method_name(parameter,parameter)的定義體,運行,當(dāng)遇見block的時候,就執(zhí)行{}中的內(nèi)容,也就是解釋器遇見method_name(parameter,parameter)時候并不關(guān)心后面的內(nèi)容,也就是block的內(nèi)容,只是在運行遇見yield statement的時候才轉(zhuǎn)到block這里,所以在class里面定義以后,調(diào)用時候必須時候,否則將出現(xiàn)錯誤: def test yield end test
結(jié)果: in `test‘: no block given (LocalJumpError)
不過這里說了:ruby解釋器,先了解method_name(parameter,parameter)就進入class definition,遇見yield就運行block,所以下面的代碼也是合法的: def test p "run" #沒有yield,也可以運行 end test{}
結(jié)果: run
Block可以接收一個來自yield處的變量,也可以返回一個值 1)Block接收值 值(value)來源于yield,例如 i1,i2 = 1,1 # parallel assignment (i1 = 1 and i2 = 1) while i1 <= max yield i1 #i1講被傳遞到block里面 i1,i2 = i2,i1+i2 end end
fib_up_to(1000){|f| print f," "} #i1的值賦值給f,表現(xiàn)就是i1的值傳遞到了block里面的f了,使用|variable|來接受值
關(guān)于block的應(yīng)用我們很久以前就說過了,假如yield有2個parameters,那么這里要用|para1,para2|這樣的形式
前面我們提過,不能從語法的角度來理解ruby,要從語意的角度理解ruby,因為ruby是一門更加貼近問題域的語言 這里yield i1有一層語意就是,我要把 i1 這個值傳遞給一個block
我們下面看一段程序: a = [1,2]
結(jié)果是: a→[1, 2] b→2 #值被括號內(nèi)改變 defined?(c)→nil #c沒有被定義,也就是c出了括號,就沒有了
事實上有2條規(guī)則: a. block外面的variables出現(xiàn)在block內(nèi)部,內(nèi)部將直接改變值 b. block外面的variables沒有出現(xiàn)在block內(nèi)部,這時候的variables作用域僅僅在這個block內(nèi)或者說是屬于這個block
這樣我們得到了block和外部環(huán)境交互的能力,但是這樣的方式也遭到了很多的質(zhì)疑,也許會改今后的版本中進行一定的調(diào)整
*注意,一般的情況,比如find,each,times這些迭代器都是從0開始到max結(jié)束
上面的總結(jié)不全面,注意,對于each,times我們根本不關(guān)注他們的返回類型
2)Block返回值 一個block可以返回一個值,可以認(rèn)為一個yield(parameter,parameter)可以返回一個值,這個值是block中,最后一次賦值的表達(dá)式的值(同于methods)
我們前面提到過find iterator,可能大家會覺得有些迷惑,它的實現(xiàn)如下: class Array def find for i in 0...self.length #self表示引用它的object value = self[i] return value if yield(value) #yield返回一個值,這里返回的是一個true or false end return nil end end
*上面的self.length可以寫成 size,表示引用它的對象的大小
self 表示應(yīng)用這個method的object,例如,在method里面有self,123.method_name 這個時候,self表示123這個object
yield 有什么好處呢?yield實現(xiàn)了代碼級的復(fù)用,我們一般來說,實現(xiàn)的是method級別的復(fù)用,也就是復(fù)用方法,而yield提供了這樣的能力,使得我們重復(fù)出現(xiàn)的代碼都消失了,這是十分神奇的
Iterator:(一般來說只要是collection就有他的iterators) 1)each 遍歷array中的所有element,對于array來說,可以這樣用: [1,2,3,4,5].each{|i| p i}
對于File class each iterator每次從file object里面每次讀出一行: f = File.open("testfile") f.each do |line| #一次讀出一行 puts line end f.close
2)collect 和each一樣進行遍歷,但是collect將所有的block的返回值收集起來,建立一個array object返回,例如: num = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] num2.each{|i| print i," "}
結(jié)果: 2 4 6 8 10 12 14
3)inject inject可以帶parameter,inject的parameter和yield的parameter有一定的關(guān)系,yield有2個parameters object.inject(a){|p1,p2| p1+p2} 這個表示p1初始化為a,p1以后的值為block的返回值,p2是object elements的值,一直遍歷過去 舉例說明: print [1,2,4,9].inject(0){|sum,ele| sum+ele} #結(jié)果:16 print [1,2,4,9].inject(1){|sum,ele| sum*ele} #結(jié)果:72
inject也可以不帶parameter,這個時候,yield第一個parameter的值為array object第一個element的值,yield第2個parameter的值是array object的第2個element的值,比如: print [1,2,4,9].inject{|sum,ele| sum*ele} #結(jié)果:72 .............1) print [1,2,4,9].inject{|sum,ele| sum+ele} #結(jié)果:16 .............2) 1)中sum初始化的值是1,ele最初值是2 2)中sum初始化的值是1,ele最初值是2
|
|