Про итераторы.
Решил снова поиграться с итераторами на довольно простом примере
package main
import (
"fmt"
"iter"
"strconv"
)
func IterSlice[V any](slc []V) iter.Seq[V] {
return func(yield func(V) bool) {
for i := range len(slc) {
if !yield(slc[i]) {
return
}
}
}
}
func Map[V any, R any](seq iter.Seq[V], fn func(V) R) iter.Seq[R] {
return func(yield func(R) bool) {
for v := range seq {
if !yield(fn(v)) {
return
}
}
}
}
func Limit[V any](seq iter.Seq[V], limit int) iter.Seq[V] {
return func(yield func(V) bool) {
i := 0
for v := range seq {
if i >= limit {
return
}
if !yield(v) {
return
}
i++
if i >= limit {
return
}
}
}
}
func main() {
slc := []int{1, 2, 3, 4, 5}
imulIter := Map(Limit(IterSlice(slc), 3), func(v int) int { return v * 2 })
strIter := Map(imulIter, func(v int) string { return strconv.Itoa(v) })
strIter = Map(strIter, func(v string) string { return "Number is " + v })
for v := range strIter {
fmt.Println(v)
}
}
Первое впечатление, это реально круто. На простом коде это скорее ведет к ухудшению восприятия кода, но на больших цепочках вызовов (например те которые берут данные из хранилища) это будет просто палочкой выручалочкой в плане потребления памяти и организации кода.
Однако есть минусы:
– Сильно ощущается отсутствие параметрических методов на типах. Я знаю причины, более того я знаю даже детали этих причин и всю подноготную сложность решения этой проблемы. Но блин, создавать отдельные переменные для вызова
Map
выглядит, прямо скажем, не очень удобно. А лесенка из скобочек напоминает лисп, но выглядит уродливо. – Итераторы
Seq[V]
и Seq2[K,V]
. Опять же ощущается отсутствие кортежей на уровне языка, т.к. получается необходимость писать функции для двух типов итераторов: с одиночными значениями и для пар ключ-значения. Про эту проблему знают, но полного решения ее похоже не предвидится. Текущий консенсус в том, что функции нужно написать всего один раз.В остальном, пока очень доволен. Надо посмотреть на производительность, но в целом это близко к революции которую произвели дженерики два года назад.
Поиграться можно тут https://go.dev/play/p/aDX94GK8rYj?v=gotip
П.С. Библиотеки для работы с итераторами уже тоже начали появляться, например https://github.com/BooleanCat/go-functional/blob/main/v2/iter/count_test.go
>>Click here to continue<<
