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