代码块和迭代器
这节简单地介绍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
仔细看下在代码块中的代码(puts "In the block")是怎么执行两次的,一个yield执行一次。
你可以在调用yield的时候给它提供参数:这些参数会传递到代码块中。在代码块内,你在两个竖线(|)之间列出要接收的参数的名字。
Code
在整个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