上篇写到C语言中的迭代器 ,今天写写Go语言中实现迭代器模式的方法。Go语言称为next C
,在实现迭代器上,完全可以使用C语言中的思路,语法上更简练。但这里不使用这种经典的方式,而是使用Go语言本身带有的特性。我们会使用它的channel特性、goroutine特性和闭包特性。为了简单表达,这里的实现依旧使用双链表,如果要迭代更复杂的数据结构,比如树型结构,只要修改迭代器的算法实现过程即可。
使用channel特性实现迭代器 迭代器创建一个goroutine在后台迭代数据结构对象,把获得的Element发送到channel上,主线程从channel的另外一端取出Element,然后完成对Element的处理。从而,把可迭代对象(数据结构)的迭代和处理分离开来。channel本身在设计上就是线程安全,因此不用添加额外的锁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package mainimport ( "container/list" "fmt" ) type Iterator chan *list.Elementfunc NewIterator (list *list.List, buffer int ) Iterator { if buffer < 0 { buffer = 0 } iter := make (Iterator, buffer) go func () { for e := list.Front(); e != nil ; e = e.Next() { iter <- e } close (iter) }() return iter } func main () { l := list.New() l.PushBack("a" ) l.PushBack("b" ) l.PushBack("c" ) l.PushBack("d" ) l.PushBack("f" ) for v := range NewIterator(list, 0 ) { fmt.Println(v.Value.(string )) }
对于其他更复杂数据结构,迭代过程在go块内实现即可。出于安全考虑,我们还可以把NewIterator
函数返回单向的channel。
上述的方法甚至可以更OOP,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 func (list *LocalList) NewIterator(buffer int ) Iterator { if buffer < 0 { buffer = 0 } iter := make (Iterator, buffer) go func () { for e := list.Front(); e != nil ; e = e.Next() { iter <- e } close (iter) }() return iter }
使用闭包实现迭代器 闭包的特性就是携带函数作用域以外的变量,这些变量可以当做闭包的状态。这样,每次调用闭包(函数),修改外部状态,当调用完毕后,函数销毁,但外部状态还存在,下次调用闭包(函数)时,可以从该状态开始继续执行。利用这种原理,每次调用闭包迭代一次,把迭代的位置(即数据结构的位置)保存到外部状态中,下次从新迭代从外部状态中恢复。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package mainimport ( "container/list" "errors" "fmt" ) func NewClosureIterator (l *list.List) func () (value *list.Element, err error ) { var current *list.Element = l.Front() var r *list.Element fn := func () (value *list.Element, err error ) { r = current if r == nil { return nil , errors.New("StopIteration" ) } current = current.Next() return r, nil } return fn } func main () { l := list.New() l.PushBack("a" ) l.PushBack("b" ) l.PushBack("c" ) l.PushBack("d" ) l.PushBack("f" ) next := NewClosureIterator(l) for { v, err := next() if err != nil { break } fmt.Println(v.Value.(string )) } }
通过上面的代码发现这种实现有点像装饰器模式,但不对,装饰器模式传入的是函数。对于更复杂的数据结构,迭代算法直接在闭包中实现即可。