Пакет iter
Обзор
Пакет iter предоставляет базовые определения и операции, связанные с итераторами последовательностей.
Итераторы
Итератор — это функция, которая передает последовательные элементы последовательности функции обратного вызова, условно называемой yield. Функция останавливается, когда последовательность заканчивается или когда yield возвращает false, указывая на необходимость досрочно завершить итерацию. Этот пакет определяет Seq и Seq2 (произносится как seek — первый слог слова sequence) в качестве сокращенных обозначений для итераторов, передающих функции yield 1 или 2 значения для каждого элемента последовательности:
type ( Seq[V any] func(yield func(V) bool) Seq2[K, V any] func(yield func(K, V) bool) )
Seq2 представляет собой последовательность пар значений, обычно пар «ключ-значение» или «индекс-значение».
Yield возвращает true, если итератору следует продолжить работу со следующим элементом последовательности, и false, если его следует остановить.
Yield вызывает панику, если его вызвать после того, как он вернул false.
Например, maps.Keys возвращает итератор, который формирует последовательность ключей карты m; он реализован следующим образом:
func Keys[Map ~map[K]V, K comparable, V any](m Map) iter.Seq[K] {
return func(yield func(K) bool) {
for k := range m {
if !yield(k) {
return
}
}
}
}
Другие примеры можно найти в блоге Go: перебор с помощью типов-функций.
Чаще всего функции-итераторы вызываются с помощью цикла range, например:
func PrintAll[V any](seq iter.Seq[V]) {
for v := range seq {
fmt.Println(v)
}
}
Соглашения об именовании
Функции и методы-итераторы называют по последовательности, по которой они выполняют обход:
// All returns an iterator over all elements in s. func (s *Set[V]) All() iter.Seq[V]
Метод-итератор типа коллекции обычно называется All, поскольку он перебирает последовательность всех значений коллекции.
Если тип содержит несколько возможных последовательностей, имя итератора может указывать на то, какая именно последовательность предоставляется:
// Cities returns an iterator over the major cities in the country. func (c *Country) Cities() iter.Seq[*City] // Languages returns an iterator over the official spoken languages of the country. func (c *Country) Languages() iter.Seq[string]
Если итератору нужны дополнительные параметры настройки, функция-конструктор может принимать дополнительные аргументы настройки:
// Scan returns an iterator over key-value pairs with min ≤ key ≤ max. func (m *Map[K, V]) Scan(min, max K) iter.Seq2[K, V] // Split returns an iterator over the (possibly-empty) substrings of s // separated by sep. func Split(s, sep string) iter.Seq[string]
Если возможны разные порядки итерации, имя метода может указывать на используемый порядок:
// All returns an iterator over the list from head to tail. func (l *List[V]) All() iter.Seq[V] // Backward returns an iterator over the list from tail to head. func (l *List[V]) Backward() iter.Seq[V] // Preorder returns an iterator over all nodes of the syntax tree // beneath (and including) the specified root, in depth-first preorder, // visiting a parent node before its children. func Preorder(root Node) iter.Seq[Node]
Одноразовые итераторы
Большинство итераторов позволяют пройти всю последовательность: при вызове итератор выполняет необходимые действия для начала обхода, затем вызывает yield для каждого следующего элемента последовательности и после этого выполняет очистку перед возвратом. Повторный вызов итератора выполняет обход последовательности заново.
Некоторые итераторы отступают от этого соглашения и позволяют пройти последовательность только один раз. Такие «одноразовые итераторы» обычно возвращают значения из потока данных, который нельзя перемотать и начать сначала. Если вызвать итератор повторно после досрочной остановки, обход потока может продолжиться, но повторный вызов после завершения последовательности не вернет ни одного значения. В комментариях документации к функциям или методам, возвращающим одноразовые итераторы, следует указать это:
// Lines returns an iterator over lines read from r. // It returns a single-use iterator. func (r *Reader) Lines() iter.Seq[string]
Получение значений
Функции и методы, принимающие или возвращающие итераторы, должны использовать стандартные типы Seq или Seq2, чтобы обеспечить совместимость с циклами range и другими адаптерами итераторов. Стандартные итераторы можно рассматривать как «итераторы с передачей значений», которые передают значения функции yield.
Иногда цикл range — не самый естественный способ получать значения последовательности. В этом случае Pull преобразует стандартный итератор с передачей значений в «итератор с получением значений», который позволяет получать из последовательности по одному значению за раз. Pull запускает итератор и возвращает пару функций — next и stop, которые соответственно возвращают следующее значение итератора и останавливают его.
Например:
// Pairs returns an iterator over successive pairs of values from seq.
func Pairs[V any](seq iter.Seq[V]) iter.Seq2[V, V] {
return func(yield func(V, V) bool) {
next, stop := iter.Pull(seq)
defer stop()
for {
v1, ok1 := next()
if !ok1 {
return
}
v2, ok2 := next()
// If ok2 is false, v2 should be the
// zero value; yield one last pair.
if !yield(v1, v2) {
return
}
if !ok2 {
return
}
}
}
}
Если вызывающий код не получает всю последовательность до конца, он должен вызвать stop, чтобы функция-итератор могла завершить работу и вернуться. Как показано в примере, обычно для этого используют defer.
Использование стандартной библиотеки
В нескольких пакетах стандартной библиотеки есть API на основе итераторов, в первую очередь в пакетах maps и slices. Например, maps.Keys возвращает итератор по ключам карты, а slices.Sorted собирает значения итератора в срез, сортирует их и возвращает срез. Поэтому для перебора отсортированных ключей карты можно написать:
for _, key := range slices.Sorted(maps.Keys(m)) {
...
}
Изменение
Итераторы предоставляют только значения последовательности, но не прямой способ ее изменить. Если итератор должен предоставлять механизм изменения последовательности во время обхода, обычно определяют тип позиции с дополнительными операциями, а затем предоставляют итератор по позициям.
Например, реализация дерева может предоставлять:
// Positions returns an iterator over positions in the sequence.
func (t *Tree[V]) Positions() iter.Seq[*Pos[V]]
// A Pos represents a position in the sequence.
// It is only valid during the yield call it is passed to.
type Pos[V any] struct { ... }
// Value returns the value at the cursor.
func (p *Pos[V]) Value() V
// Delete deletes the value at this point in the iteration.
func (p *Pos[V]) Delete()
// Set changes the value v at the cursor.
func (p *Pos[V]) Set(v V)
Затем вызывающий код может удалить из дерева ненужные значения следующим образом:
for p := range t.Positions() {
if boring(p.Value()) {
p.Delete()
}
}
Указатель
Файлы пакета
iter.go
func Pull 1.23
func Pull[V any](seq Seq[V]) (next func() (V, bool), stop func())
Pull преобразует последовательность итератора seq «с передачей значений» в итератор «с получением значений», доступный через две функции: next и stop.
Next возвращает следующее значение последовательности и логическое значение, указывающее, является ли это значение допустимым. Когда последовательность заканчивается, next возвращает нулевое значение V и false. Вызывать next после достижения конца последовательности или после вызова stop допустимо. В этих случаях функция продолжит возвращать нулевое значение V и false.
Stop завершает итерацию. Ее необходимо вызвать, когда вызывающему коду больше не нужны следующие значения и next еще не сообщил об окончании последовательности (вернув false). Вызывать stop несколько раз, а также после того, как next уже вернул false, допустимо. Обычно вызывающий код должен использовать «defer stop()».
Одновременный вызов next или stop из нескольких горутин считается ошибкой.
Если итератор вызывает панику во время вызова next (или stop), то сама функция next (или stop) вызывает панику с тем же значением.
func Pull2 1.23
func Pull2[K, V any](seq Seq2[K, V]) (next func() (K, V, bool), stop func())
Pull2 преобразует последовательность итератора seq «с передачей значений» в итератор «с получением значений», доступный через две функции: next и stop.
Next возвращает следующую пару последовательности и логическое значение, указывающее, является ли эта пара допустимой. Когда последовательность заканчивается, next возвращает пару нулевых значений и false. Вызывать next после достижения конца последовательности или после вызова stop допустимо. В этих случаях функция продолжит возвращать пару нулевых значений и false.
Stop завершает итерацию. Ее необходимо вызвать, когда вызывающему коду больше не нужны следующие значения и next еще не сообщил об окончании последовательности (вернув false). Вызывать stop несколько раз, а также после того, как next уже вернул false, допустимо. Обычно вызывающий код должен использовать «defer stop()».
Одновременный вызов next или stop из нескольких горутин считается ошибкой.
Если итератор вызывает панику во время вызова next (или stop), то сама функция next (или stop) вызывает панику с тем же значением.
type Seq
Seq — это итератор последовательностей отдельных значений. При вызове в виде seq(yield) функция seq вызывает yield(v) для каждого значения v в последовательности и останавливается досрочно, если yield возвращает false. Подробнее см. в документации пакета iter.
type Seq[V any] func(yield func(V) bool)
type Seq2
Seq2 — это итератор последовательностей пар значений, чаще всего пар «ключ-значение». При вызове в виде seq(yield) функция seq вызывает yield(k, v) для каждой пары (k, v) в последовательности и останавливается досрочно, если yield возвращает false. Подробнее см. в документации пакета iter.
type Seq2[K, V any] func(yield func(K, V) bool)
© Google, Inc.
Licensed under the Creative Commons Attribution License 3.0.
http://golang.org/pkg/iter/