javascript语言扩展:可迭代对象(5)

文章1-4篇说的都是js中的可迭代对象,下面让我们看看ruby中的等价物。

不可否认,ruby中对于迭代器和生成器的语法都相当简洁;ruby从一开始就有一个简洁的基因,而js后来的不断扩充使得其有些语法比较“别扭”和“奇怪”,虽说ruby也不比js小几岁啊!(官方的说法是一个1992年一个1995年)

在ruby中遍历一个数组的代码如下所示:

ary = (1..10).to_a
ary.each {|x| puts x}
#你也可以直接写
(1..10).to_a.each {|x|puts x}

而在ruby中范围对象本身是可枚举的(可遍历差不多意思),所以你可以直接这么写:

(1..10).each {|x|puts x}

在ruby中写一个迭代器,也是相当简单:

def itor(x)
    10.times {|i|yield x*i}
end
#调用迭代器后面直接跟代码块
itor(11) {|x|puts x}
#返回
=begin
0
11
22
33
44
55
66
77
88
99
=end

而ruby中的枚举器和js中的可迭代对象类似,枚举器是类Enumerable::Enumerator的实例,一般通过Object类的to_enum或其同义词enum_for方法来创建连如果调用时没有提供参数则to_enum返回一个枚举器,该枚举器的each方法只是简单调用目标对象的each方法,枚举器的一个用途是建立防御式拷贝:

(1..10).to_enum.each {|x|puts x}

如果to_enum中第一个实参是一个符号,则它标识了一个迭代器方法,枚举器的each方法调用那个迭代器方法:

s.to_enum(:each_byte).each {|x|puts x}
=begin
返回
104
101
108
108
111
32
119
111
114
108
100
=end
2.1.5 :016 > s.to_enum(:each_char).each {|x|puts x}
=begin
返回
h
e
l
l
o

w
o
r
l
d
=end

而在ruby1.9之后又对枚举器的语法做了修改使其更加简洁,当以不带代码块的方式调用内建迭代器方法时,会自动返回一个枚举器,因此上述代码可以简写为:

s.each_char.each {|x|puts x}

要想对自定义的迭代器方法实现如上行为,可以通过返回self.to_enum来实现:

def itor(x)
    if block_given?
        10.times {|i|yield x*i}
    else
        self.to_enum(:itor,x)
    end
end
#我们可以这样调用
itor(11) {|x|puts x}
#也可以这样调用
itor(11).each {|x| puts x}

简单解释下self.to_enum(:itor,x)这句,因为我为了测试方便是在全局上下文中定义的函数itor,所以它自动成为全局对象main的私有方法,而该方法也会成为Object类的私有实例方法:

def ps
    puts "hi"
end
class A
    def sh
        ps
    end
end
a = A.new
a.sh #显示"hi:

在执行self.to_enum(:itor,x)时,当时的self是main对象,即将返回枚举器的each方法与itor函数对应上。

ruby里面也有类似的for in循环,不过我们一般都不怎么用呢:

for x in itor(11)
    puts x
end
#和itor(11).each {|x|puts x}代码返回一样

枚举器可以实现外部迭代,实际上枚举器也称为外部迭代器。可以使用Kernel.loop方法包裹枚举器,从而在StopIteration异常抛出时自动退出循环:

itor = 9.downto(1)
loop {print "#{itor.next}, "}
#打印8, 7, 6, 5, 4, 3, 2, 1,

在ruby中枚举器自带了rewind方法使外部迭代器重新开始迭代。

最后,在ruby中我们可以用纤程(Fiber)来实现所谓的生成器,虽然这比较晦涩:

def fibonacci_generator
    Fiber.new do
        x,y = 0,1
        loop do
            Fiber.yield y
            x,y = y,x+y
        end
    end
end
g = fibonacci_generator
10.times {print g.resume, " "}
上一篇:VIM下CS命令


下一篇:Android Phonebook编写联系人UI加载及联系人保存流程(三)