在Ruby的一切都是一个对象。甚至一个代码块也是一个对象!在Ruby中,代码对象被称为代码块。你可以把代码块想像成小的程序单元。它们包含 Ruby代码并且能够在执行它们时转入到方法中。在Python,C和Java中与之类似的概念是函数指针,匿名函数,内部类和回调函数。
Ruby代码块的语法是,把Ruby代码放在大括号之间或放在do/end命令之间。如下所示:
{ #这是一个代码块... } do #...并且这也是一个代码块 end |
在一个很简单的实例中,{puts"hello world"}就是一个有效的代码块。那么,你应该如何使用这些代码块并且把它们作为一组代码传递给一个方法呢?为此,首先要定义一个如下形式的简单方法:
def someMethod yield end |
命令yield把控制传递给代码块(它被传递给这个方法)。下面的代码向你展示,一个代码块是如何被传递到上面这个方法的。
irb(main):001:0> someMethod {puts "hello world"} hello world |
每当调用yield时,执行传递到该方法的代码块。下面是另一个更复杂些的方法的例子,它使用了一个代码块来做更多的工作。
irb(main):001:0> def fibonacci (stop) while stop < 20 stop=yield end end => nil irb(main):006:0> i=0; j=1; fibonacci(j) {puts i; temp = i; i = j;j = temp + j} 0 1 1 2 3 5 8 |
代码块被应用于整个Ruby中。最重要的是,代码块被Ruby内部地应用于象Array,Hash,甚至String这样一些类的iterator方法 中。一个代码块其实就是,你定义要执行什么任务(典型地,是针对一个元素的任务)。为了进一步说明代码块和iterator的使用,我们提供了一个小例 子。假定你定义了一些场院动物类(如图3所示),还有一个这些动物的数组,你可以通过在这个数组上使用一个代码块和一个迭代子来实现让每一种动物发声。
irb(main):031:0> barnYard = [Cow.new, Duck.new, Chicken.new, Horse.new, Dog.new] => [#<Cow:0x58d2f48>, #<Duck:0x58d2f30>, #<Chicken:0x58d2f00>, #<Horse:0x58d2ee8>, #<Dog:0x58d2ed0>] irb(main):032:0> barnYard.each {|animal| animal.talk} Mooooo Quack Cluck-cluck Naaaay Bark bark |
在barnYard上调用的每个方法正是一个数组上的iterator方法之一。注意,|符号用于定义要接收的参考。在本例情况中,代码块接收一个参数;也就是把barnYard中的每一种动物作为迭代子来遍历这个动物集合。
迭代子和代码块甚至允许我们完成如下一些相当简单和整洁的事情:
irb(main):001:0> 3.times {puts "Ruby is cool!"} Ruby is cool! Ruby is cool! Ruby is cool! |
是的,即使是Integer类也提供了一个迭代子(times方法)-它使用一个代码块来快速地执行循环操作。
在离开有关代码块和迭代子的讨论前,你还应该知道代码块可以被赋值给一个变量。事实上,这样的代码块实际上是Proc类的实例。你可以使用new方法来定义一个Proc实例,或者使用这些Proc实例-这些实例稍后可以使用call方法来调用。
irb(main):003:0> simpleProc.call hello => nil irb(main):004:0> anotherProc.call("hello yourself") hello yourself => nil |
这允许一个可重用的代码块可以被包装为一个对象(它可以在任何地方执行)而进行传递。下面是显示Ruby中可重用代码的示例!
irb(main):001:0> def simpleMethod(aProc) puts "Is Ruby cool or what?" aProc.call("Way cool dude!") end => nil irb(main):005:0> simpleMethod(anotherProc) |
Ruby酷不酷?相当不错!