Muitas pessoas parecem ficar confusas com os iteradores recém-adicionados no Go, por isso decidi escrever outro artigo tentando explicá-los da maneira mais simples possível.
Primeiro, acho importante entender como os iteradores estão sendo chamados e usados pelo Go, e na verdade é bem simples, vamos usar o iterador slices.All como exemplo. Aqui está como você normalmente usaria este iterador:
package main import ( "fmt" "slices" ) func main() { slice := []string{ "Element 1", "Element 2", "Element 3", "Element 4", } for index, element := range slices.All(slice) { if index >= 2 { break } fmt.Println(index, element) } // Output: // 0 Element 1 // 1 Element 2 }
E aqui está como realmente se parece:
package main import ( "fmt" "slices" ) func main() { slice := []string{ "Element 1", "Element 2", "Element 3", "Element 4", } slices.All(slice)(func (index int, element string) bool { if index >= 2 { return false // break } fmt.Println(index, element) return true // continue loop as normal }) // Output: // 0 Element 1 // 1 Element 2 }
O que acontece é que o corpo do loop é "movido" para produzir a função que é passada para o iterador, enquanto continue e break estão sendo transformados para retornar true e retornar false respectivamente. return true também é adicionado ao final do loop para sinalizar que gostaríamos de obter o próximo elemento, se nada mais tomasse outra decisão antes.
Este não é o desdobramento exato do que o compilador está fazendo e não verifiquei a implementação do Go para verificar isso, mas eles produzem resultados equivalentes a partir de minhas observações.
Agora que você entendeu como eles estão sendo chamados e percebeu como isso realmente é simples, será muito mais fácil entender como criar seu próprio iterador e sua execução.
Vamos criar um iterador de depuração que imprimirá mensagens de depuração para cada etapa da implementação do iterador que percorrerá todos os elementos da fatia (funcionalidade slices.All).
Primeiro, criarei uma pequena função auxiliar para desconectar a mensagem com o tempo de execução atual.
import ( "fmt" "time" ) var START time.Time = time.Now() func logt(message string) { fmt.Println(time.Since(START), message) }
Voltar ao iterador:
import ( "iter" ) func DebugIter[E any](slice []E) iter.Seq2[int, E] { logt("DebugIter called") // the same way iter.All returned function // we called in order to iterate over slice // here we are returning a function to // iterate over all slice elements too return func(yield func(int, E) bool) { logt("Seq2 return function called, starting loop") for index, element := range slice { logt("in loop, calling yield") shouldContinue := yield(index, element) if !shouldContinue { logt("in loop, yield returned false") return } logt("in loop, yield returned true") } } }
Eu adicionei algumas instruções de impressão de depuração para que pudéssemos ver melhor a ordem de execução do iterador e como ele reagirá a diferentes palavras-chave, como break e continue.
Finalmente, vamos usar o iterador implementado:
func main() { slice := []string{ "Element 1", "Element 2", "Element 3", "Element 4", } for index, element := range DebugIter(slice) { message := "got element in range of iter: " element logt(message) if index >= 2 { break } if index > 0 { continue } time.Sleep(2 * time.Second) logt("ended sleep in range of iter") } }
Nos dará o resultado:
11.125µs DebugIter called 39.292µs Seq2 return function called, starting loop 42.459µs in loop, calling yield 44.292µs got element in range of iter: Element 1 2.001194292s ended sleep in range of iter 2.001280459s in loop, yield returned true 2.001283917s in loop, calling yield 2.001287042s got element in range of iter: Element 2 2.001291084s in loop, yield returned true 2.001293125s in loop, calling yield 2.0012955s got element in range of iter: Element 3 2.001297542s in loop, yield returned false
Este exemplo mostra muito bem como os iteradores funcionam e são executados. Ao usar o iterador no loop de intervalo, todas as instruções no bloco de loop são "movidas" para uma função chamada rendimento. Quando chamamos yield, essencialmente pedimos ao go runtime para executar qualquer coisa localizada no bloco de loop com o seguinte valor para esta iteração, é também por isso que yield será bloqueado, se o corpo do loop for bloqueado. Caso o tempo de execução determine que esta iteração do loop deve parar, o rendimento retornará falso, isso pode acontecer quando a palavra-chave break for atendida durante a execução do bloco de loop, não devemos mais chamar o rendimento se isso acontecer. Caso contrário, devemos continuar chamando yield.
Código completo:
package main import ( "fmt" "time" "iter" ) var START time.Time = time.Now() func logt(message string) { fmt.Println(time.Since(START), message) } func DebugIter[E any](slice []E) iter.Seq2[int, E] { logt("DebugIter called") // the same way iter.All returned function // we called in order to iterate over slice // here we are returning a function to // iterate over all slice elements too return func(yield func(int, E) bool) { logt("Seq2 return function called, starting loop") for index, element := range slice { logt("in loop, calling yield for") shouldContinue := yield(index, element) if !shouldContinue { logt("in loop, yield returned false") return } logt("in loop, yield returned true") } } } func main() { slice := []string{ "Element 1", "Element 2", "Element 3", "Element 4", } for index, element := range DebugIter(slice) { message := "got element in range of iter: " element logt(message) if index >= 2 { break } if index > 0 { continue } time.Sleep(2 * time.Second) logt("ended sleep in range of iter") } // unfold compiler magic // DebugIter(slice)(func (index int, element string) bool { // message := "got element in range of iter: " element // logt(message) // if index >= 2 { // return false // } // if index > 0 { // return true // } // time.Sleep(2 * time.Second) // logt("ended sleep in range of iter") // // return true // }) }
Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.
Copyright© 2022 湘ICP备2022001581号-3