一文帶大家搞懂Go語言中的迭代器
1. 迭代器簡介
1.1 定義
維基百科上迭代器的定義:迭代器(英語:iterator),是使用戶可在容器對象(container,例如鏈表或數(shù)組)上遍訪的對象,設(shè)計(jì)人員使用此接口無需關(guān)心容器對象的內(nèi)存分配的實(shí)現(xiàn)細(xì)節(jié)。
1.2 為何需要迭代器
理論上,迭代器的功能可以通過其他方法實(shí)現(xiàn),那為何還需要迭代器呢?迭代器之所以存在,自然是有它的優(yōu)點(diǎn)的:
- 節(jié)省內(nèi)存: 迭代器不會一次性將所有數(shù)據(jù)都加載到內(nèi)存中,而是按需生成數(shù)據(jù)。比如在逐行讀取大文件時(shí)使用迭代器能顯著減少內(nèi)存的使用。
- 性能優(yōu)化:迭代器僅在需要時(shí)生成數(shù)據(jù),可以減少不必要的計(jì)算。比如,數(shù)據(jù)庫查詢結(jié)果集逐行返回,而不是一次性返回所有記錄。
- 解耦數(shù)據(jù)生成邏輯與遍歷邏輯:迭代器負(fù)責(zé)生成數(shù)據(jù),其邏輯是獨(dú)立的,不受遍歷數(shù)據(jù)邏輯的影響。同樣的 ,遍歷迭代器時(shí)無需考慮數(shù)據(jù)生成的細(xì)節(jié)。
2. Go 1.23版本之前的迭代器
在 1.23 版本之前,Go 官方是沒有提供傳統(tǒng)意義上的迭代器的,如果要用到迭代器的功能,就需要自己實(shí)現(xiàn)。下面來說說兩種比較有代表性的實(shí)現(xiàn)。
面向?qū)ο笳Z言的迭代器
在面向?qū)ο笳Z言中,Java 中的 Iterator
迭代器是比較有代表性的,下面使用 Go 來實(shí)現(xiàn)一個(gè)類似的迭代器。
package main import "fmt" type Iterator struct { data []int index int } func NewIterator(data []int) *Iterator { return &Iterator{data: data, index: 0} } func (it *Iterator) HasNext() bool { return it.index < len(it.data) } func (it *Iterator) Next() int { if it.index >= len(it.data) { panic("沒有元素了") } item := it.data[it.index] it.index++ return item } func main() { it := NewIterator([]int{1, 2, 3, 4, 5}) for it.HasNext() { fmt.Println(it.Next()) } }
這種面向?qū)ο髮?shí)現(xiàn)的迭代器不夠簡潔,一般而言,在實(shí)際編程中很少自定義這樣的迭代器。
函數(shù)形式的迭代器
Go 中使用函數(shù)形式實(shí)現(xiàn)的迭代器,比較有代表性的是使用協(xié)程和通道來實(shí)現(xiàn)的:
package main import "fmt" func generator(num int) <-chan int { ch := make(chan int) go func() { for i := 0; i < num; i++ { ch <- i } close(ch) }() return ch } func main() { for n := range generator(5) { fmt.Println(n) } }
這種形式的迭代器,相對面向?qū)ο蟮牡饕啙嵉枚唷2贿^該方式使用了協(xié)程來實(shí)現(xiàn),其性能要差很多。
3. Go 1.23版本的迭代器
Go 1.23 版本提供了一個(gè) iter 包,算是 Go 官方提供了一個(gè)標(biāo)準(zhǔn)的迭代器實(shí)現(xiàn)。
Go 1.23 的迭代器是一個(gè)函數(shù)類型,把另一個(gè)函數(shù)( yield
函數(shù))作為參數(shù),定義迭代器時(shí)將元素傳遞給 yield
函數(shù)。迭代器函數(shù)會在以下兩種情況停止。
- 在序列迭代結(jié)束后停止,表示此次迭代結(jié)束。
- 在
yield
函數(shù)返回false
時(shí)停止,表示提前停止迭代。
下面使用 iter 包來實(shí)現(xiàn)函數(shù)形式的迭代器,iter.Seq[V any]
是 func(yield func(V) bool)
的簡稱:
package main import ( "fmt" "iter") func generator(num int) iter.Seq[int] { return func(yield func(int) bool) { for i := 0; i < num; i++ { if !yield(i) { return } } } } func main() { for n := range generator(5) { fmt.Println(n) } }
這種形式的迭代器,兼顧了性能和簡潔性。
在性能方面,筆者測試了一下,使用 yield
函數(shù) 實(shí)現(xiàn)的迭代器,比使用協(xié)程和通道實(shí)現(xiàn)的迭代器,提升了 200 多倍的性能!
簡潔性是相對面向?qū)ο髞碚f的。有些遺憾的是,相比其他的一些語言,上面的實(shí)現(xiàn)在簡潔明了方面做得不夠好。generator
需要返回一個(gè)函數(shù),該函數(shù)的參數(shù)還是一個(gè)函數(shù),還需要判斷是否提前停止迭代。這樣很不優(yōu)雅,本來這些是需要隱藏到幕后,由語言本身實(shí)現(xiàn)而不是由用戶去實(shí)現(xiàn)的。
其他語言中一個(gè)比較優(yōu)雅的迭代器實(shí)現(xiàn)方式是 Python 的生成器函數(shù),其方式如下:
def generator(num: int): for i in range(num): yield i
上面的代碼僅用3行就實(shí)現(xiàn)了同樣的功能,而且該函數(shù)里面無需返回函數(shù),無需判斷是否需要提前停止迭代。用戶只需關(guān)注如何生成數(shù)據(jù),然后通過一個(gè) yield
語句就可將數(shù)據(jù)傳給遍歷者。
4. 總結(jié)
Go 1.23版本之前,Go 官方?jīng)]有提供迭代器,需要用戶自己定義,很不方便。Go 1.23 開始,終于提供了官方的迭代器,彌補(bǔ)了 Go 語言在迭代器方面的缺失,方便了有相關(guān)需求的用戶,這是可喜的。
Go 語言追求簡單,少即是多的理念,這本無可厚非,不過之前的 Go 追求得有些極端了。近年來,Go 終于沒那么極端了,為 Go 語言本身和 Go 標(biāo)準(zhǔn)庫 添加了許多特性和功能。望 Go 早日成為主流編程語言。
到此這篇關(guān)于一文帶大家搞懂Go語言中的迭代器的文章就介紹到這了,更多相關(guān)Go語言迭代器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語言操作數(shù)據(jù)庫及其常規(guī)操作的示例代碼
這篇文章主要介紹了Go語言操作數(shù)據(jù)庫及其常規(guī)操作的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04如何使用大學(xué)教育郵箱下載golang等軟件(推薦)
這篇文章主要介紹了如何使用大學(xué)教育郵箱下載goland等軟件,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09Go 請求兔子識別接口實(shí)現(xiàn)流程示例詳解
這篇文章主要為大家介紹了Go 請求兔子識別接口實(shí)現(xiàn)流程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04