关于迭代器的理解
B05高端研讨会第一期
时间:12月21日。
与会:professor yang、always hurry、old fake
正文
迭代器应该是我们在编程中使用的最多的设计模式,但是我们因为常见而常常忽略她的存在,我们就来探究一下一行foreach语句背后所蕴含的道理。
起点:遍历容器
在没有迭代器的时候,我们想遍历一个容器里的内容应该怎么办?如果是一个数组,我们只需要累加索引然后依次访问就可以了。对于链表我们要拿到第一个元素,然后再通过第一个元素拿到下一个元素,依次往下,直到最后一个元素。如果我们往下想,那么所有的容器都需要有一个单独对应的遍历方法,这显然是不合理的,设计第一步就是要把可以独立的概念抽离出来,那么遍历一个容器行为,就是需要我们抽离的概念。
迭代器的意义
迭代器的意义就在于 在不暴露一个容器内部表示的情况下,顺序遍历一个容器。
迭代器的具象
在军训中 我们都站过队列,报过数,我把报数这个行为,作为迭代器的具象。想象一下 如果教官需要知道当前军训的有多少人,我们只要挨个报数就行了,每一个军训的学员 只要知道 我自己是谁, 下一个是谁,当最后一个学员报完的时候,遍历军训学员 这个容器的操作 就算完成了。无论军训学员站成几排几列,站成圆形还是方形,只要每一个学员知道自己是谁,下一个是谁,都可以完成这个操作。
迭代器模式的实现
在C#中我们可以找到 跟军训学员对应的实现,
public interface IEnumerator
{
object Current { get; }
bool MoveNext();
void Reset();
}
对于 Current 属性,就是表示当前报数的这个学员是谁,MoveNext 就是要知道我下一个有没有人,如果有人就把报数的顺序交给她。
在C#中,所有的容器,都实现了这个借口,所以我们才能使用foreach遍历。
迭代器中的角色
我们说要把遍历一个容器的行为抽象出来,仅仅是容器实现了对应的接口,明显还不够,因为我们缺角色。其实上例中提到的IEnumerator 接口,准确的定位应该是迭代器工厂,用于生产迭代器对象的。
当迭代器对象被生产出来,还需要另一个角色来唤醒她,这个角色就是迭代者,就像我们在军训时的教官一样,负责宣布开始,叫停。
在C#中,foreach就是迭代者的角色。
我们分析一下以下语句:
Dictionary<int, int> dic = new Dictionary<int, int>();
foreach (var item in dic)
{
}
这个语法中,有dic这个容器提供迭代器对象,由foreach语句不停地访问当前的对象,并且让当前的迭代器对象执行MoveNext 操作。
在Lua中的实现
在Lua中也有迭代器的实现,迭代器工厂是ipairs、pairs,迭代者是for语句。由于Lua是弱类型的语言,所以没有明确的迭代器的类型定义,但是只要符合了她的标准的方法,都可以作为迭代器工厂使用。
下面是我用closure实现的一个最简单的迭代器模式:
总结
以下是我们讨论总结的思维导图。