迭代器并不是Ruby发明的.它广泛地运用于各种面向对象语言.在Lisp中也有,只是不这么叫罢了.尽管如此,迭代器的概念并不为许多人熟悉,因此我们将在此做较为详细的介绍.
你知道,动词iterate的意思是做同一件事许多遍,因此,iterator就是用来将同一件事做许多次的东西.
当我们写代码时,我们需要各种环境下的循环.在C里,我们用for或者while.比如,
char*str;
for(str="abcdefg";*str!='\0';str++){
/*processacharacterhere*/
}
C的for(...)语法提供了一种写循环的抽象方法,但测试*str是否为空(null)字符需要程序员了解字符串内部结构的细节.这让C看起来像低级(low-level)语言.更高级的语言是通过它们更具弹性的迭代器支持来实现的.考虑下面的sh命令行脚本:
#!/bin/sh
foriin*.[ch];do
#...herewouldbesomethingtodoforeachfile
done
当前目录下所有的C源文件和头文件都将被处理,由命令行shell来一个个地捡取文件名并处理其中的细节.我想这是在比C要高的级别上工作,你觉得呢?
但有更多值得我们考虑的:在一种语言能够很好的给内建的数据类型的提供迭代器的同时,我们却仍需要回去用低级别的循环语言来实现对自己定义的数据类型的迭代,这真是让人失望.在面对对象编程时,用户经常一个接一个地定义数据类型,因此这是一个很严重的问题.
因此,所有的OOP语言都包含了一定的迭代器机制.某些语言为此提供一种特殊的类;Ruby则允许我们直接定义迭代器.
Ruby的String类型有很多有用的迭代器:
ruby>"abc".each_byte{|c|printf"<%c>",c};print"\n"
each_byte是个用于字符串中每个字符的迭代器.每个字符串由局部变量c代替.这可以翻译为类似C的代码...
ruby>"a\nb\nc\n".each_line{|l|printl}
采用迭代器,这将很轻松的取代C的大多数编程效果(找换行符,生成子串等等)
前面出现的for语句通过each迭代器实现迭代功能.String的each和each_line的工作原理差不多,让我们用for重写上面的例子:
我们可以用retry流程控制语句连接迭代循环,它会从头执行当前循环的迭代.
yield有时会在一个迭代器的定义中出现.yield将流程控制移至传递给迭代器的代码域(这将会在过程对象那一节介绍更多的细节).下面的例子定义了一个repeat迭代器,会依参数的设置执行多次代码域.
利用retry,我们可以定义一个有while相同作用的迭代器,虽然在实际应用中它太慢了.