ruby迭代:ruby 迭代器使用思路方法

迭代器并不是Ruby发明.它广泛地运用于各种面向对象语言.在Lisp中也有,只是不这么叫罢了.尽管如此,迭代器概念并不为许多人熟悉,因此我们将在此做较为详细介绍.
你知道,动词 iterate 意思是做同件事许多遍,因此,iterator就是用来将同件事做许多次东西.
当我们写代码时,我们需要各种环境下循环.在C里,我们用for或者while.比如,
char *str;
for (str = "abcdefg"; *str != '\0'; str) {
/* process a character here */
}

Cfor(...)语法提供了种写循环抽象思路方法,但测试 *str 是否为空(null)需要员了解串内部结构细节.这让C看起来像低级(low-level)语言.更高级语言是通过它们更具弹性迭代器支持来实现.考虑下面 sh 命令行脚本:
#!/bin/sh
for i in *.[ch]; do
# ... here would be something to do for each file
done

当前目录下所有C源文件和头文件都将被处理,由命令行shell来个个地捡取文件名并处理其中细节.我想这是在比 C 要高级别上工作,你觉得呢?
但有更多值得我们考虑:在种语言能够很好给内建数据类型提供迭代器同时,我们却仍需要回去用低级别循环语言来实现对自己定义数据类型迭代,这真是让人失望.在面对对象编程时,用户经常个接个地定义数据类型,因此这是个很严重问题.
因此,所有OOP语言都包含了迭代器机制.某些语言为此提供种特殊类;Ruby则允许我们直接定义迭代器.
RubyString类型有很多有用迭代器:
ruby> "abc".each_{|c| prf "<%c>", c}; pr "\n"
<a><b><c>
nil

each_ 是个用于串中每个迭代器.每个串由局部变量c代替.这可以翻译为类似C代码...
ruby> s="abc";i=0
0
ruby> while i<s.length
| prf "<%c>", s[i]; i1
| end; pr "\n"
<a><b><c>
nil

...然而, each_ 迭代器在概念上要简单些,而且即使以后 String 类突然有所改变也应该可以照样工作.使用迭代器个好处便是在此类改变中仍然可以保持健壮;,它确是好代码个特点.(好,请有点儿耐心,我们将会马上谈到什么是类)
String个迭代器是 each_line.
ruby> "a\nb\nc\n".each_line{|l| pr l}
a
c
nil

采用迭代器,这将很轻松取代C大多数编程效果(找换行符,生成子串等等)
前面出现for语句通过each迭代器实现迭代功能. Stringeach和each_line工作原理差不多,让我们用for重写上面例子:
ruby> for l in "a\nb\nc\n"
| pr l
| end
a
c
nil

我们可以用retry流程控制语句连接迭代循环,它会从头执行当前循环迭代.
ruby> c=0
0
ruby> for i in 0..4
| pr i
| i 2 and c 0
| c = 1
| pr "\n"
| retry
| end
| end; pr "\n"
012
01234
nil

yield有时会在个迭代器定义中出现. yield将流程控制移至传递给迭代器代码域(这将会在过程对象那节介绍更多细节).下面例子定义了个repeat迭代器,会依参数设置执行多次代码域.
ruby> def repeat(num)
| while num > 0
| yield
| num -= 1
| end
| end
nil
ruby> repeat(3) { pr "foo\n" }
foo
foo
foo
nil

利用retry,我们可以定义个有while相同作用迭代器,虽然在实际应用中它太慢了.
ruby> def WHILE(cond)
| not cond
| yield
| retry
| end
nil
ruby> i=0; WHILE(i<3) { pr i; i1 }
012 nil

搞懂什么是迭代器了吗?有些限制,但你可以写自己迭代器;实际上,当你定义个新数据类型时,为它定义个合适迭代器经常也很方便.这样看来,上面例子并不是很好用.在我们理解了类以后,我们可以讨论讨论更具实际意义迭代器.
Tags:  迭代器 迭代方法 迭代器的使用 ruby迭代

延伸阅读

最新评论

发表评论