代碼塊和迭代器
這節簡單地介紹Ruby的其中一個特長。我們將要學習的是代碼塊:一些能和方法關聯在一起調用的代碼,它們簡直就像是方法的參數一樣。
這是一個難以置信的強大特性。我們其中一個評論家對這個特性是這樣評論的:“這真的很有趣,而且很重要,如果以前你沒有注意到它,
那麼你現在就要開始關注了。”我們都同意他的觀點。
你可以使用代碼塊實現回調(但它們比較Java的匿名內嵌函式簡單多了),傳入一個代碼塊(但它們比較C的方法指標要安全得多),然後實現迭代。
代碼塊僅僅是一些在花括弧或do...end間的代碼。
{ puts "Hello" } # this is a block
do ###
club.enroll(person) # and so is this
person.socialize #
end ###
為什麼會有兩種分隔字元呢?其中一個原因是有時候會覺得其中一個比另一個用得更自然。另一個原因是它們的優先順序不同:花括弧比較do/end對優先順序更高。
在本書中,我們將盡量按照即將成為Ruby標準的用法,單行代碼使用花括弧,多行代碼使用do/end。
一旦你建立了一個代碼塊,你就能把它和方法一起關聯調用。只要把代碼塊放在包含方法調用的代碼後面就行了。
例如,下面的代碼中,一個包含puts "Hi"的代碼和一個greet方法關聯調用。
greet { puts "Hi" }
如果方法包含有參數,把它們放在花括弧的前面。
verbose_greet("Dave", "loyal customer") { puts "Hi" }
使用Ruby的yield,一個與方法關聯的代碼塊能執行一次或多次。你可以把yeild想象成一種方法的調用,它能調用與包含有yield的方法相關聯的代碼塊。
下面通過實際的例子來示範。我們定義一個調用了yield兩次的方法,把代碼塊放在和方法同一行上,在方法的調用後面(並且是在方法的所有參數後面)。
Code
def call_block
puts "Start of method"
yield
yield
puts "End of method"
end
call_block { puts "In the block" }
produces:
Start of method
In the block
In the block
End of method
仔細看下在代碼塊中的代碼(puts "In the block")是怎麼執行兩次的,一個yield執行一次。
你可以在調用yield的時候給它提供參數:這些參數會傳遞到代碼塊中。在代碼塊內,你在兩個豎線(|)之間列出要接收的參數的名字。
Code
def call_block
yield("hello", 99)
end
call_block {|str, num| }
在整個Ruby類庫中都是使用代碼塊實現迭代:一種能從各種集合,如數組中,返回連續的元素的方法。
animals = %w( ant bee cat dog elk ) # create an array
animals.each {|animal| puts animal } # iterate over the contents
produces:
ant
bee
cat
dog
elk
讓我們來看一下上面的例子中,數組類是怎樣使用代碼塊實現each迭代的。each迭代迴圈數組中的每個元素並調用yield。
在虛擬碼中,它們可能看起來會像這樣
# within class Array
def each
for each element # <not valid Ruby
yield(element)
end
end
許多程式設計語言如C和Java內建的迴圈結構在Ruby中都不過是簡單的方法調用,調用0次或多次與方法關聯的代碼塊。
[ 'cat', 'dog', 'horse' ].each {|name| print name, " " }
5.times { print "*" }
3.upto(6) {|i| print i }
('a'..'e').each {|char| print char }
produces:
cat dog horse *****3456abcde
這裡,我們讓對象5調用代碼塊5次,讓對象3調用一個代碼塊,並給它傳遞從3到6的連續值。最後,從a到e這個範圍的字元分別調用代碼塊各一次。
輸入和輸出
Ruby內建有一個綜合的I/O庫。然而,在本書的大部分例子中,我們仍然使用一些簡單的方法。我們已經使用了兩個用於輸出的方法。
puts輸出它的參數,並轉到下一行。print也是輸出它的參數,但它不會換行。它們都能用於輸出到任意的I/O對象,但預設是輸出到標準輸出中。
另一個我們用得比較多的輸出方法是printf,它把參數在格式字串的控制下輸出(和C或Perl中的printf一樣)。
printf("Number: %5.2f,\nString: %s\n", 1.23, "hello")
produces:
Number: 1.23,
String: hello
在這個例子中,格式化字串"Number: %5.2f,\nString: %s\n"告訴printf使用一個浮點數(總共允許5個字元,其中兩個在小數點後面)
和一個字串替代。注意到分行符號(\n)在這個字串內;每個分行符號都會使用輸出換到下一行。
你可以通過多種途徑把輸入讀到程式中。可能最經典的是使用常用的gets,它返回標準輸入資料流的下一行到程式中。
line = gets
print line