2008-07-22

在Ruby中实现迭代器

关键字: ruby 迭代器
初学Ruby时,对它的迭代器一知半解,这两天在李刚的Ruby on Rails敏捷开发最佳实践中看到这方面很详细的讲解,才明白在Ruby中实现迭代器原来是件很简单的事情。现在把我的理解写出来作为一个纪录。


所谓迭代器,实际上就是一个能接受代码块的方法。对于迭代器方法而言,它能接受一个代码块作为参数。
下面定义一个Apple类,在该Apple类中,定义一个迭代器,该迭代器负责输出该Apple实例的三个实例变量。下面是Apple类的代码:
# 定义一个Apple类 
class Apple 
    # 定义Apple类的构造器 
    def initialize(name, color, weight) 
        @name = name; 
        @color = color; 
        @weight = weight; 
end 
    # 使用存储器定义了三个属性 
    attr :name 
    attr :color 
    attr :weight 
    # 定义一个show方法,该show方法可以作为Apple类的迭代器 
    def show 
        yield :name , @name 
        yield :color , @color 
        yield :weight , @weight 
    end 
end 
# 创建一个Apple实例 
apple = Apple.new("红苹果" , "红色" , "0.4") 
# 使用apple的show迭代器 
apple.show do |name , value| 
    puts "这个苹果的" + name.to_s + "变量值是:"  + value.to_s 
end 
上面的代码定义了一个Apple类,在该Apple类里定义了一个show方法,从表明上看,该show方法与普通方法并没有太大的不同,但在show方法里包含了yield调用,这表明该方法可以接受代码块参数——一个可以接受代码块参数的方法就是迭代器。
运行上面的方法,看到如下结果:
引用
这个苹果的name变量值是:红苹果
这个苹果的color变量值是:红色
这个苹果的weight变量值是:0.4

从上面运行结果中可以看出,通过使用迭代器可以非常方便地遍历某个实例的内部变量。

根据上面的执行过程,可发现了迭代器方法和块之间有如下传递关系:块被当成一个特殊参数传给迭代器方法,而迭代器方法内部在使用yield调用代码块时可将参数值传入块。

代码块也可以返回一个结果给调用它的方法。这个块中的最后一个表达式的值将会返回给方法,Array中的find方法就是这样工作的,下面就是Array中定义的find方法。
class Array 
    # 为Array增加一个find方法 
    def find 
        # 使用for循环迭代数组的所有数组元素 
        for i in 0...size  
            value = self 
            # yield调用时将数组元素值传入代码块 
            # 调用结束后将代码块的返回值(boolean值)传回该方法体 
            return value if yield value 
        end  
        return nil  
    end  
end 
# 使用数组的find方法 
puts [1, 3, 5, 7, 9].find {|v| v * v > 30 } 
在上面的代码中有一个值得注意的地方:因为Ruby是动态语言,因此当定义Array类时,是为系统的Array类增加一个find方法,而不是覆盖系统的Array类。
在上面的find方法定义中,方法体内将数组元素依次传入指定的块,如果这个块返回true,则这个方法返回当前对应的元素值;如果没有符合的值,则返回nil。
通过上面的迭代器,可以发现迭代器的优势:Array类只做自己应该做的——访问数组元素,而对数组元素所做的处理,则包含在代码块中,并允许动态指定!
实际上,迭代器的功能就是一种回调!迭代器方法所属的类只负责遍历需要遍历的元素,而对元素所做的处理则通过回调代码块来实现。
Ruby中的容器对象(如数组、Range和Hash对象等)都包含了两个简单的迭代器,分别是each和collect。each可以认为是最简单的迭代器,它会对集合的每个元素调用块。
看如下用法:
["Hello", "Ruby" , "Rails"].each do |value| 
    puts "数组元素的值是 " + value 
end 

上面的迭代器each负责迭代数组中的每个元素,把该元素的值传入代码块。
执行上面的程序,看到如下运行结果:
引用
数组元素的值是 Hello
数组元素的值是 Ruby
数组元素的值是 Rails
另一个迭代器是collect,它跟each类似,将容器中的元素传递给一个块,在块中处理后返回一个包含处理结果的新数组。
看如下代码:
a = ["Hello", "Ruby" , "Rails"].collect do |value| 
    # 把每个数组元素的字母全部大写 
    value.upcase 
end 
p a 
执行上面的程序,看到如下运行结果:
引用
["HELLO", "RUBY", "RAILS"]
评论
free_dem
搜索本博客
博客分类
最近加入圈子
最新评论
评论排行榜