我們來簡單地描述Ruby的一個獨特特性。Block,一種可以和方法調用相關聯的代碼塊,幾乎就像參數一樣。這是一個不可思議的功能強大的特性。
可以用Block實現回調(但它比Java的匿名內部(anonymous inner)類更簡單),傳遞一組代碼(但它遠比c的函數指針靈活),以及實現迭代器。
Block只是在花括號或者do...end之間的一組代碼。
1
2
3
4
5
6
7
8
9
|
{puts "Hello" } #this is a block do ### club.enroll(person) #and so is this person.socialize # end ### |
為什么有兩種分界符?部分原因是有人覺得有時候用一種分界符比另外一種感覺更自然。另外一部分原因是它們有不同的優先級:花括號比do/end綁定的更緊密些。我們嘗試遵循正在成為Ruby標準的一個約定俗成,單行block用花括號,多行block用do/end。
一旦創建了block,就可以與方法的調用相關聯。把block的開始放在含有方法調用的源碼行的結尾處,就可以實現關聯。比如,在下面的代碼中,含有puts "Hi" 的block與greet方法的調用相關聯。
1
|
greet {puts "Hi" } |
如果方法有參數,它們出現在block之前。
1
|
verbose_greet( "Dave" , "loyal customer" ){puts "Hi" } |
然后使用Ruby的yield語句,方法可以一次或多次地調用(invoke)相關聯的block。可以把yield想象成比如方法調用,它調用含有yield語句的方法所關聯的block。
下面的例子顯示了如何使用yield語句。定義了一個方法,它會調用yield兩次。然后調用這個方法,把block放在同一行,在方法調用之后(并在方法的所有參數之后)。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
def call_block puts "Start of method" yield yield puts "End of method" end call_block{puts "In the block" } |
輸出結果:
1
2
3
4
5
6
7
|
Strat of method In the block In the block End of method |
可以提供參數給yield的調用;參數會傳遞到block中。在block中,豎線(|)之間給出參數名來接受這些來自yield的參數。
1
2
3
4
5
6
7
|
def call_block yield ( "Hello" , 99 ) end call_block {|str,num| ...} |
在Ruby庫中大量使用了block來實現迭代器;迭代器是從某種收集(collection)如數組中連續返回元素的方法。
1
2
3
|
animals = %w(ant bee cat dog elk) #創建一個數組 animals. each {|animal| puts animal} #迭代它的內容 |
輸出結果:
1
2
3
4
5
6
7
8
9
|
ant bee cat dog elk |
讓我們看一下如何實現應用在前面例子中的Array類中的each迭代器。each迭代器循環處理數組中的元素,對每個元素調用yield。在偽碼中,它可能寫成:
1
2
3
4
5
6
7
8
9
10
11
|
#在Array類中...... def each for each element #<--無效的Ruby語句 yield (element) end end |
許多內建于c和java等語言的循環結構在Ruby中只是方法調用,這些方法會零次或多次地調用相關聯的block。
1
2
3
4
5
6
7
|
[ 'cat' , 'dog' , 'horse' ]. each {|name| print name, " " } 5 .times {print "*" } 3 .upto( 6 ){|i| print i} ( 'a' .. 'e' ). each {|char| print char} |
輸出結果:
1
|
cat dog horse *****3456abcde |
上面的代碼要求對象5 五次調用block;然后要求對象3調用一個block,并傳入一個連續的值,直到這個值到達6為止。最后對a到e的字符區間(range),使用each方法調用block。