Files
lancet/iterator/iterator.go
donutloop 473f9c9f3e Iterator: general refactoring and reset method (#193)
Feature

Reset allows for the iteration process over a sequence to be restarted from the beginning.
It enables reusing the iterator for multiple traversals without needing to recreate it.

Refactoring

It is a idiomatic practice to design functions and methods to return concrete struct types.
This approach promotes flexibility and decoupling, allowing the calling code to work with any implementation that satisfies the interface
2024-02-29 11:30:12 +08:00

224 lines
6.3 KiB
Go

// Copyright 2022 dudaodong@gmail.com. All rights resulterved.
// Use of this source code is governed by MIT license
// Package iterator provides a way to iterate over values stored in containers.
// note:
// 1. Full feature iterator is complicated, this package is just a experiment to explore how iterators could work in Go.
// 2. The functionality of this package is very simple and limited, may not meet the actual dev needs.
// 3. It is currently under development, unstable, and will not be completed for some time in the future.
// So, based on above factors, you may not use it in production. but, anyone is welcome to improve it.
// Hope that Go can support iterator in future. see https://github.com/golang/go/discussions/54245 and https://github.com/golang/go/discussions/56413
package iterator
import (
"context"
"golang.org/x/exp/constraints"
)
// Iterator supports iterating over a sequence of values of type `E`.
type Iterator[T any] interface {
// Next checks if there is a next value in the iteration or not
HasNext() bool
// Next returns the next value in the iteration if there is one,
// and reports whether the returned value is valid.
// Once Next returns ok==false, the iteration is over,
// and all subsequent calls will return ok==false.
Next() (item T, ok bool)
}
// ResettableIterator supports to reset the iterator
type ResettableIterator[T any] interface {
Iterator[T]
// Reset allows for the iteration process over a sequence to be restarted from the beginning.
// It enables reusing the iterator for multiple traversals without needing to recreate it.
Reset()
}
// StopIterator is an interface for stopping Iterator.
type StopIterator[T any] interface {
Iterator[T]
// Stop indicates that the iterator will no longer be used.
// After a call to Stop, future calls to Next may panic.
// Stop may be called multiple times;
// all calls after the first will have no effect.
Stop()
}
// DeleteIter is an Iter that implements a Delete method.
type DeleteIterator[T any] interface {
Iterator[T]
// Delete deletes the current iterator element;
// that is, the one returned by the last call to Next.
// Delete should panic if called before Next or after
// Next returns false.
Delete()
}
// SetIterator is an Iter that implements a Set method.
type SetIterator[T any] interface {
Iterator[T]
// Set replaces the current iterator element with v.
// Set should panic if called before Next or after
// Next returns false.
Set(v T)
}
// PrevIterator is an iterator with a Prev method.
type PrevIterator[T any] interface {
Iterator[T]
// Prev moves the iterator to the previous position.
// After calling Prev, Next will return the value at
// that position in the container. For example, after
// it.Next() returning (v, true)
// it.Prev()
// another call to it.Next will again return (v, true).
// Calling Prev before calling Next may panic.
// Calling Prev after Next returns false will move
// to the last element, or, if there are no elements,
// to the iterator's initial state.
Prev()
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Functions that create an Iterator from some other type. //
////////////////////////////////////////////////////////////////////////////////////////////////////
// FromSlice returns an iterator over a slice of data.
func FromSlice[T any](slice []T) *SliceIterator[T] {
return &SliceIterator[T]{slice: slice, index: -1}
}
func ToSlice[T any](iter Iterator[T]) []T {
result := []T{}
for item, ok := iter.Next(); ok; item, ok = iter.Next() {
result = append(result, item)
}
return result
}
type SliceIterator[T any] struct {
slice []T
index int
}
func (iter *SliceIterator[T]) HasNext() bool {
return iter.index < len(iter.slice)-1
}
func (iter *SliceIterator[T]) Next() (T, bool) {
iter.index++
ok := iter.index >= 0 && iter.index < len(iter.slice)
var item T
if ok {
item = iter.slice[iter.index]
}
return item, ok
}
// Prev implements PrevIterator.
func (iter *SliceIterator[T]) Prev() {
if iter.index == -1 {
panic("Next function should be called Prev")
}
if iter.HasNext() {
iter.index--
} else {
iter.index = len(iter.slice) - 1
}
}
// Set implements SetIterator.
func (iter *SliceIterator[T]) Set(value T) {
if iter.index == -1 {
panic("Next function should be called Set")
}
if iter.index >= len(iter.slice) || len(iter.slice) == 0 {
panic("No element in current iterator")
}
iter.slice[iter.index] = value
}
func (iter *SliceIterator[T]) Reset() {
iter.index = -1
}
// FromRange creates a iterator which returns the numeric range between start inclusive and end
// exclusive by the step size. start should be less than end, step shoud be positive.
func FromRange[T constraints.Integer | constraints.Float](start, end, step T) *RangeIterator[T] {
if end < start {
panic("RangeIterator: start should be before end")
} else if step <= 0 {
panic("RangeIterator: step should be positive")
}
return &RangeIterator[T]{start: start, end: end, step: step, current: start}
}
type RangeIterator[T constraints.Integer | constraints.Float] struct {
start, end, step, current T
}
func (iter *RangeIterator[T]) HasNext() bool {
return iter.current < iter.end
}
func (iter *RangeIterator[T]) Next() (T, bool) {
if iter.current >= iter.end {
var zero T
return zero, false
}
num := iter.current
iter.current += iter.step
return num, true
}
func (iter *RangeIterator[T]) Reset() {
iter.current = iter.start
}
// FromChannel creates an iterator which returns items received from the provided channel.
// The iteration continues until the channel is closed.
func FromChannel[T any](channel <-chan T) *ChannelIterator[T] {
return &ChannelIterator[T]{channel: channel}
}
type ChannelIterator[T any] struct {
channel <-chan T
}
func (iter *ChannelIterator[T]) Next() (T, bool) {
item, ok := <-iter.channel
return item, ok
}
func (iter *ChannelIterator[T]) HasNext() bool {
return len(iter.channel) == 0
}
// ToChannel create a new goroutine to pull items from the channel iterator to the returned channel.
func ToChannel[T any](ctx context.Context, iter Iterator[T], buffer int) <-chan T {
result := make(chan T, buffer)
go func() {
defer close(result)
for item, ok := iter.Next(); ok; item, ok = iter.Next() {
select {
case result <- item:
case <-ctx.Done():
return
}
}
}()
return result
}