traversal

package
v0.0.0-...-9392aba Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 25, 2024 License: MIT Imports: 2 Imported by: 0

Documentation

Overview

Package traversal provides generic Iterator and Generator interfaces.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func AsChannel

func AsChannel[T any](it Iterator[T], ctx context.Context) <-chan T

AsChannel returns a channel that receives each element of the underlying iterator. The channel and underlying iterator are closed once the iterator has no more values, or the context is cancelled. Note that a cancellation of the context might not be registered until the Next method of the iterator has returned.

The caller should ensure that the returned channel is drained, to ensure the iterator is garbage collected.

Example
package main

import (
	"context"
	"fmt"

	"github.com/tkw1536/pkglib/traversal"
)

func main() {
	it := traversal.Slice([]int{0, 1, 2, 3, 4, 5})

	for i := range traversal.AsChannel(it, context.Background()) {
		fmt.Print(i, " ")
	}

}
Output:

0 1 2 3 4 5

func Drain

func Drain[Element any](it Iterator[Element]) ([]Element, error)

Drain iterates all values in it until no more values are returned. All returned values are stored in a slice which is returned to the caller.

Example
package main

import (
	"fmt"

	"github.com/tkw1536/pkglib/traversal"
)

func main() {
	source := traversal.Slice([]int{0, 1, 2, 3, 4, 5})
	fmt.Println(traversal.Drain(source))
}
Output:

[0 1 2 3 4 5] <nil>

func Pipe

func Pipe[Element any](dst Generator[Element], src Iterator[Element]) (ok bool)

Pipe pipes elements from src into dst. If any error occurs in src, the same error is sent to dst.

A return value of false indicates that the iterator requested cancellation, ok an error occurred. In such a case, the caller should not continue the use of dst.

Example
package main

import (
	"fmt"

	"github.com/tkw1536/pkglib/traversal"
)

func main() {
	iterator := traversal.New(func(generator traversal.Generator[int]) {
		source := traversal.Slice([]int{0, 1, 2, 3, 4, 5})
		traversal.Pipe(generator, source)
	})

	for iterator.Next() {
		fmt.Println(iterator.Datum())
	}
	fmt.Println(iterator.Err())
}
Output:

0
1
2
3
4
5
<nil>

Types

type Generator

type Generator[T any] interface {
	// Yield yields an item to the receiving end.
	// A return value of false indicates that the receiving end has been closed and the generator should stop producing values early.
	Yield(datum T) bool

	// YieldError yields an error to the receiving end.
	// Calling YieldError(nil) has no effect.
	//
	// If the receiving end of this iterator requested cancellation, the return value is false.
	// Otherwise, if the return value indicates if a non-nil error has been passed.
	YieldError(err error) bool

	// Returned indicates if the Return method was called.
	Returned() bool

	// Return closes this generator.
	//
	// Calling Return multiple times is an error.
	// Calls to Yield and YieldError after Return are also an error.
	Return()
}

Generator is the opposite of Iterator, allowing values to be sent to a receiving iterator. Methods on a generator may not be called concurrently.

type Iterator

type Iterator[T any] interface {
	// Next advances this iterator to the next value.
	// The returned value indicates if there are further values.
	Next() bool

	// Datum returns the current value of this iterator.
	Datum() T

	// Err returns any error that occurred during iteration.
	Err() error

	// Close closes this iterator, indicating to the sender that no more
	// values would be received.
	Close() error
}

Iterator represents an object that can be iterated over. An iterator is not safe for concurrent access.

A user of an iterator must ensure that the iterator is closed once it is no longer needed. A user should furthermore use the Err() method to check if an error occurred.

A typical use of an iterator would be something like:

var it Iterator[T] // get the iterator from somewhere
defer it.Close()

for it.Next() {
  datum := it.Datum()
  _ = datum // ... do something with datum ...
}

if err := it.Err(); err != nil {
  return err // an error occurred!
}

func Connect

func Connect[Element1, Element2 any](source Iterator[Element1], f func(element Element1, sender Generator[Element2]) (ok bool)) Iterator[Element2]

Connect creates a new iterator that calls f for every element returned by source. If the pipe function returns false, iteration over the original elements stops.

Example
package main

import (
	"fmt"

	"github.com/tkw1536/pkglib/traversal"
)

func main() {
	source := traversal.Slice([]int{0, 1, 2})
	dest := traversal.Connect(source, func(element int, sender traversal.Generator[int]) (ok bool) {
		sender.Yield(2 * element)
		sender.Yield(2*element + 1)
		return true
	})

	for dest.Next() {
		fmt.Println(dest.Datum())
	}
	fmt.Println(dest.Err())
}
Output:

0
1
2
3
4
5
<nil>

func Empty

func Empty[Element any](err error) Iterator[Element]

Empty returns a new iterator that contains no elements. The Err method will return err, which may be nil.

Example
package main

import (
	"fmt"

	"github.com/tkw1536/pkglib/traversal"
)

func main() {
	thing := traversal.Empty[any](nil)
	fmt.Println(thing.Next())
	fmt.Println(thing.Err())

}
Output:

false
<nil>
Example (Error)
package main

import (
	"errors"
	"fmt"

	"github.com/tkw1536/pkglib/traversal"
)

func main() {
	thing := traversal.Empty[any](errors.New("some error"))
	fmt.Println(thing.Next())
	fmt.Println(thing.Err())

}
Output:

false
some error

func FromChannel

func FromChannel[T any](in <-chan T) (Iterator[T], context.Context)

FromChannel creates a new iterator that receives values from the provided channel. The context is cancelled once the receiving end of the iterator requests cancellation or the input channel is exhausted.

NOTE: Even if the receiving iterator end requests cancellation, the input channel will always be drained. This ensures that any process sending to the iterator can always continue to send values.

Example
package main

import (
	"fmt"

	"github.com/tkw1536/pkglib/traversal"
)

func main() {
	// fill a channel with some numbers
	// and then close it!
	in := make(chan int, 6)
	for i := range cap(in) {
		in <- i
	}
	close(in)

	// create a channel
	c, _ := traversal.FromChannel(in)

	for c.Next() {
		fmt.Print(c.Datum(), " ")
	}

}
Output:

0 1 2 3 4 5

func Map

func Map[Element1, Element2 any](source Iterator[Element1], f func(Element1) Element2) Iterator[Element2]

Map creates a new iterator that produces the same values as source, but mapped over f. If source produces an error, the returned iterator also produces the same error.

Example
package main

import (
	"fmt"

	"github.com/tkw1536/pkglib/traversal"
)

func main() {
	iterator :=
		traversal.Map(
			traversal.Slice([]int{1, 2, 3, 4, 5}),
			func(value int) bool {
				return value%2 == 0
			},
		)
	for iterator.Next() {
		fmt.Println(iterator.Datum())
	}
	fmt.Println(iterator.Err())
}
Output:

false
true
false
true
false
<nil>

func New

func New[T any](source func(generator Generator[T])) Iterator[T]

New creates a new iterator generator pair and returns the iterator.

The generator is passed to the function source. Once source returns, the return method on the generator is called if it has not been already.

Example
package main

import (
	"fmt"

	"github.com/tkw1536/pkglib/traversal"
)

func main() {
	iterator := traversal.New(func(generator traversal.Generator[string]) {
		if !generator.Yield("hello") {
			return
		}
		if !generator.Yield("world") {
			return
		}
	})
	defer iterator.Close()

	for iterator.Next() {
		fmt.Println(iterator.Datum())
	}
	fmt.Println(iterator.Err())

}
Output:

hello
world
<nil>
Example (Close)
package main

import (
	"fmt"

	"github.com/tkw1536/pkglib/traversal"
)

func main() {
	iterator := traversal.New(func(generator traversal.Generator[string]) {
		if !generator.Yield("hello") {
			return
		}

		if !generator.Yield("world") {
			return
		}

		panic("never reached")
	})
	defer iterator.Close()

	// request a single item, then close the iterator!
	for iterator.Next() {
		fmt.Println(iterator.Datum())
		iterator.Close()
	}

	fmt.Println(iterator.Err())

}
Output:

hello
<nil>
Example (Error)
package main

import (
	"errors"
	"fmt"

	"github.com/tkw1536/pkglib/traversal"
)

func main() {
	iterator := traversal.New(func(generator traversal.Generator[string]) {
		if !generator.Yield("hello") {
			return
		}

		generator.YieldError(errors.New("something went wrong"))
	})
	defer iterator.Close()

	for iterator.Next() {
		fmt.Println(iterator.Datum())
	}

	fmt.Println(iterator.Err())

}
Output:

hello
something went wrong

func Sequence

func Sequence[T any](seq RangeFunc[T]) Iterator[T]

Sequence creates a new iterator from the given RangeFunc.

Example
package main

import (
	"fmt"

	"github.com/tkw1536/pkglib/traversal"
)

func main() {
	seq := traversal.RangeFunc[int](func(yield func(int) bool) {
		if !yield(42) {
			return
		}
		if !yield(69) {
			return
		}
	})

	// turn it into an iterator and drain it!
	it := traversal.Sequence(seq)
	fmt.Println(traversal.Drain(it))

}
Output:

[42 69] <nil>

func Slice

func Slice[T any](elements []T) Iterator[T]

Slice creates a new Iterator that yields elements from the given slice

Example
package main

import (
	"fmt"

	"github.com/tkw1536/pkglib/traversal"
)

func main() {
	iterator := traversal.Slice([]int{1, 2, 3, 4, 5})
	for iterator.Next() {
		fmt.Println(iterator.Datum())
	}
	fmt.Println(iterator.Err())
}
Output:

1
2
3
4
5
<nil>

type RangeFunc

type RangeFunc[V any] func(yield func(V) bool)

RangeFunc is an iterator over sequences of individual values. When called as seq(yield), seq calls yield(v) for each value v in the sequence, stopping early if yield returns false.

NOTE: This corresponds to the "iter".Seq type from the rangefunc experiment. At some point in the future when rangefunc is stable, and generic aliases are implemented, this will be replaced with an alias to "iter".Seq. See golang issues #61405 and #46477.

func Range

func Range[T any](iter Iterator[T]) RangeFunc[T]

Range turns an iterator into a function compatible with the RangeFunc experiment.

Example
package main

import (
	"fmt"

	"github.com/tkw1536/pkglib/traversal"
)

func main() {
	// create a simple iterator
	it := traversal.New(func(generator traversal.Generator[int]) {
		if !generator.Yield(42) {
			return
		}
		if !generator.Yield(69) {
			return
		}
	})

	rg := traversal.Range(it)
	rg(func(value int) bool {
		fmt.Println(value)
		return true
	})

}
Output:

42
69

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
JackTT - Gopher 🇻🇳