iter

package
v2.10.0 Latest Latest
Warning

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

Go to latest
Published: May 17, 2023 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package iter implements iteration over sequences, including lazy evaluation.

To avoid confusion, between the higher-order function "map" and the Go map data structure, the former will always be referred to as a "map function" and the latter will always be referred to as a "map collection".

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func All

func All[X any](
	f func(X) bool,
	it It[X],
) bool

All calls function f for each value x produced by an iterator until either f returns false, or the iterator is exhausted. All returns true iff f(x) was true for every x. If the iterator produced no values (was empty), then All still returns true.

func Any

func Any[X any](
	f func(X) bool,
	it It[X],
) bool

Any calls function f for each value x produced by an iterator until either f(x) returns true, or the iterator is exhausted. It returns true iff f(x) was true for at least one x.

func AppendToSlice

func AppendToSlice[X any](
	dest []X,
	xs It[X],
) []X

AppendToSlice appends every value produced by an iterator to dest, and returns the modified dest (like the builtin append).

func Check

func Check[X any](
	f func(X) error,
	it It[X],
) (X, error)

Check calls function f for each value produced by an iterator. It halts when a non-nil error is returned by f, and immediately returns the value being examined at the time and the error. Otherwise, returns a zero value and a nil error.

See also Walk, which is similar but does not check for errors.

func Count

func Count[X any](
	f func(X) bool,
	it It[X],
) (numTrue, numFalse, total int)

Count calls function f for each value x produced by an iterator. It returns a 3-tuple containing a count of the number of times f(x) returned true, a count of the number of times f(x) returned false, and the total number of times f(x) was called. If f is nil, acts as if f(x) => true for all x.

func Exhaust

func Exhaust[X any](it It[X])

Exhaust consumes an iterator and discards the produced values. As a special case, a nil iterator safely returns immediately.

func InsertToMap

func InsertToMap[X comparable, Y any](
	dest map[X]Y,
	choose func(key X, original Y, new Y) Y,
	kvs It[Pair[X, Y]],
)

InsertToMap modifies a [builtin.Map] (of Go type map[X]Y, not to be confused with the higher order function Map). For each Pair produced by the input iterator, Pair.Key is used as a map key and Pair.Value is used as the matching value.

If a key already exists in the destination map (either originally, or as a result of the input iterator producing two values with the same key), then the choose function is called with the key, the original value, and the new value. The return value of that function is used as the value to keep. If the choose function is nil, the new value in the case of conflicts is always the one most recently produced by the iterator.

func Join

func Join[In any, Out any](j Joiner[In, Out], it It[In]) Out

Join uses a Joiner to build a result by walking over an iterator.

func Reduce

func Reduce[X any](
	initial X,
	f func(X, X) X,
	it It[X],
) X

Reduce applies function f to each element of the sequence (in order) with the previous return value from f as the first argument and the element as the second argument (for the first call to f, the supplied initial value is used instead of a return value), returning the final value returned by f.

If the input iterator is empty, the result is the initial value.

func ToMap

func ToMap[X comparable, Y any](
	choose func(key X, original Y, new Y) Y,
	kvs It[Pair[X, Y]],
) map[X]Y

ToMap returns a new [builtin.map] (of Go type map[X]Y, not to be confused with the higher order function Map). For each Pair produced by the input iterator, Pair.Key is used as a map key and Pair.Value is used as the matching value. If the iterator produces two Items with the same Pair.Key, only the last value is kept.

If a key already exists in the destination map (as a result of the input iterator producing two values with the same key), then the choose function is called with the key, the original value, and the new value. The return value of that function is used as the value to keep. If the choose function is nil, the new value in the case of conflicts is always the one most recently produced by the iterator.

Example
package main

import (
	"fmt"

	lazy "github.com/tawesoft/golib/v2/iter"
)

func main() {
	type ID string
	type Person struct {
		id   ID
		name string
		age  int
	}

	// given a list of people, we want a map (id -> person)
	people := lazy.FromSlice([]Person{
		{id: "ATUR001", name: "Alice Turing", age: 23},
		{id: "GHOP001", name: "George Hopper", age: 60},
		{id: "FKAH001", name: "Freddy Kahlo", age: 29},
	})

	// for a person input, this function returns (id, person)
	personToTuple := func(person Person) lazy.Pair[ID, Person] {
		return lazy.Pair[ID, Person]{person.id, person}
	}

	// apply the function over all people (lazily...)
	peopleTuples := lazy.Map(personToTuple, people)

	// finally generate a dict
	peopleByID := lazy.ToMap(nil, peopleTuples)

	// printer function
	p := func(lookup map[ID]Person, id ID) {
		if person, ok := lookup[id]; ok {
			fmt.Printf("%s: %+v\n", id, person)
		} else {
			fmt.Printf("%s: NOT FOUND\n", id)
		}
	}

	p(peopleByID, "ATUR001")

}
Output:

ATUR001: {id:ATUR001 name:Alice Turing age:23}

func ToSlice

func ToSlice[X any](xs It[X]) []X

ToSlice returns a slice of every value produced by an iterator. If the input iterator is exhausted, returns an empty slice (not nil).

func ToString

func ToString(it It[rune]) string

ToString returns a string from an iterator that produces runes.

func Walk

func Walk[X any](
	f func(X),
	it It[X],
)

Walk calls a visitor function f for each value produced by an iterator.

See also Check, which is like Walk but aborts on error, and WalkFinal, which is like Walk but the last item produced by the input iterator is detected.

Example
package main

import (
	"fmt"

	"strings"

	lazy "github.com/tawesoft/golib/v2/iter"
)

func main() {
	var sb strings.Builder

	strings := lazy.FromSlice([]string{"one", "two", "three"})

	lazy.Walk(func(x string) { sb.WriteString(x) }, strings)

	fmt.Println(sb.String())

}
Output:

onetwothree

func WalkFinal

func WalkFinal[X any](
	f func(X, bool),
	it It[X],
)

WalkFinal is like Walk, but the second argument to the visitor function is true if x is the last item to be produced by an iterator.

Types

type FinalValue

type FinalValue[X any] struct {
	Value   X
	IsFinal bool
}

FinalValue is a value returned by the Final function. Iff IsFinal.IsFinal is true, Value is the last value produced and the iterator is now exhausted.

type It

type It[X any] func() (X, bool)

It is an iterator, defined as a function that produces a (possibly infinite) sequence of (value, true) tuples through successive calls. If the sequence has ended then returned tuple is always (undefined, false).

In this package, an iterator returning a sequence of (value, true) tuples is said to be "producing" values. An iterator returning (undefined, false) is said to be "exhausted". The sequence of values produced by an iterator is said to go from "left to right" where the leftmost value is the first one returned, and the rightmost value is the last one returned. Something that causes an iterator to produce values is called a "consumer". The number of values produced is the length of an iterator. Iterators may have infinite length.

For example, if it := It.FromSlice([]int{1, 2, 3}), then it is an iterator of length three that produces the sequence 1, 2, 3. The leftmost value is 1. The rightmost value is 3. The first call to it() returns (1, true) and is said to have produced the value 1, the second call to it() returns (2, true) and is said to have produced the value 2, the third call to it() returns (3, true) and is said to have produced the value 3, the fourth call to it() returns (0, false) and the iterator is said to be exhausted, and successive calls to it() while exhausted continue to always return (0, false). A second iterator, e.g. it2 := It.Take(5, it1), is an example of an iterator that consumes a production of the input iterator (in this case, it1) whenever it2 produces values.

func Cat

func Cat[X any](its ...It[X]) It[X]

Cat (for "concatenate") returns an iterator that merges several input iterators, consuming an input iterator in its entirety to produce values before moving on to the next input iterator. The input iterators should not be used anywhere else once provided to this function.

For example, given an iterator abc that produces the letters 'a', 'b', 'c', and an iterator def that produces the letters 'd', 'e', 'f', Cat(abc, def) will return an iterator that consumes abc and def to produce the letters 'a', 'b', 'c', 'd', 'e', 'f'.

For an iterator that produces values from each input in lockstep, consuming one value from each iterator in turn, see Zip or ZipFlat.

Some libraries call this function "chain" instead.

func Counter

func Counter[I constraints.Integer](start I, step I) It[I]

Counter returns an iterator that produces a series of integers, starting at the given number, and increasing by step each time. It terminates at the maximum representable value for the number type.

func CutString

func CutString(in string, sep rune) It[string]

CutString is like [Cut], but operates on a string, producing strings delimited by a separator rune.

For example, CutString("a|b|c", "|") returns an iterator that produces the strings "a", "b", "c".

Unlike some Go functions, CutString does not also treat invalid Unicode or invalid Utf8 byte sequences as a valid delimiter when the separator is utf8.RuneError. That is, the utf8.RuneError delimiter only matches bytes that literally encode the Unicode value of utf8.RuneError.

Note that in many situations, it is probably faster, simpler, clearer, and more idiomatic to use a bufio.Scanner. Prefer CutString only if you are combining the result iterator with other higher-order functions in this package.

func CutStringStr

func CutStringStr(in string, sep string) It[string]

CutStringStr is like CutString, but the delimiter is a string, not just a single rune.

func Empty

func Empty[X any]() It[X]

Empty returns an iterator that is typed, but empty.

func Enumerate

func Enumerate[X any, Y Pair[int, X]](
	it It[X],
) It[Y]

Enumerate produces a Pair for each value produced by the input iterator, where Pair.Key is an integer that starts at zero and increases by one with each produced value. The input iterator should not be used anywhere else once provided to this function.

For example, for an iterator abc that produces the runes 'a', 'b', 'c', Enumerate(abc) produces the values Pair[0, 'a'], Pair[1, 'b'], Pair[2, 'c'].

func Filter

func Filter[X any](
	f func(X) bool,
	it It[X],
) It[X]

Filter returns an iterator that consumes an input iterator and only produces those values where the provided filter function f returns true.

As a special case, if f is nil, it is treated as the function f(x) => true.

For example:

function isOdd := func(x int) bool { return x % 2 == 1 }
Filter(isOdd, FromSlice([]int{1, 2, 3})) // produces 1 and 3

func Final

func Final[X any](it It[X]) It[FinalValue[X]]

Final produces a FinalValue for each value produced by the input iterator. FinalValue.IsFinal is true iff value is the last value produced before the input iterator would be exhausted. If the input iterator was exhausted at the time of input, then the returned iterator is empty. The input iterators should not be used anywhere else once provided to this function.

For example, Final(FromSlice([]int{1, 2, 3}) produces the values (FinalValue{1, false}, true), (FinalValue{2, false}, true), (FinalValue{3, true}, true), (FinalValue{}, false).

func FromMap

func FromMap[X comparable, Y any](kvs map[X]Y) It[Pair[X, Y]]

FromMap returns an iterator that produces each (key, value) pair from the input [builtin.Map] (of Go type map[X]Y, not to be confused with the higher order function Map) as an Pair. Do not modify the underlying map's keys until no longer using the returned iterator.

Example
package main

import (
	"fmt"

	lazy "github.com/tawesoft/golib/v2/iter"
)

func main() {
	type Person struct {
		name string
		age  int
	}
	type ID string

	// a map of people
	people := lazy.FromMap(map[ID]Person{
		"ATUR001": {name: "Alice Turing", age: 23},
		"GHOP001": {name: "George Hopper", age: 60},
		"FKAH001": {name: "Freddy Kahlo", age: 29},
	})

	// this filter function returns true for people under thirty
	underThirty := func(kv lazy.Pair[ID, Person]) bool {
		return kv.Value.age < 30
	}

	// apply the filter and finally generate a dict
	peopleUnderThirty := lazy.ToMap(nil, lazy.Filter(underThirty, people))

	// printer function
	p := func(lookup map[ID]Person, id ID) {
		if person, ok := lookup[id]; ok {
			fmt.Printf("%s: %+v\n", id, person)
		} else {
			fmt.Printf("%s: NOT FOUND\n", id)
		}
	}

	p(peopleUnderThirty, "ATUR001")
	p(peopleUnderThirty, "GHOP001") // missing!
	p(peopleUnderThirty, "FKAH001")

}
Output:

ATUR001: {name:Alice Turing age:23}
GHOP001: NOT FOUND
FKAH001: {name:Freddy Kahlo age:29}

func FromSlice

func FromSlice[X any](xs []X) It[X]

FromSlice returns an iterator that produces each item in the input slice. Do not modify the underlying slice or backing array until no longer using the returned iterator.

func FromString

func FromString(s string) It[rune]

FromString returns an iterator that produces the runes of string s.

func Func

func Func[X any](f func() (X, bool)) It[X]

Func performs a type cast that returns an iterator (type It) from any function meeting the iterator interface.

Example
package main

import (
	"fmt"

	lazy "github.com/tawesoft/golib/v2/iter"
)

func main() {
	// generate an infinite sequence of integers with a function
	integers := func() lazy.It[int] { // or func() func() (int, bool) {
		i := 0
		return func() (int, bool) {
			result := i
			i = i + 1
			return result, true
		}
	}

	integerGenerator := lazy.Func(integers())
	firstFour := lazy.Take(4, integerGenerator)

	fmt.Printf("First four integers are: %v\n", lazy.ToSlice(firstFour))

}
Output:

First four integers are: [0 1 2 3]

func Keys added in v2.8.5

func Keys[X comparable, Y any](xs It[Pair[X, Y]]) It[X]

func Map

func Map[X any, Y any](
	f func(X) Y,
	it It[X],
) It[Y]

Map returns an iterator that consumes an input iterator (of type X) and produces values (of type Y) for each input value, according to some mapping function f(x X) => y Y.

For example:

double := func(i int) int { return i * 2 }
Map(double, FromSlice([int]{1, 2, 3})) // produces 2, 3, 6.

For example, changing the type of the result:

stringify := func(i int) string { return fmt.Sprintf("%d", i) }
Map(stringify, FromSlice([int]{1, 2, 3})) // produces "1", "2", "3".
Example
package main

import (
	"fmt"
	"strconv"

	lazy "github.com/tawesoft/golib/v2/iter"
)

func main() {
	numbersAsStrings := lazy.FromSlice([]string{"1", "2", "3", "4"})

	// atoi returns the integer x from the string "x"
	atoi := func(s string) int {
		i, _ := strconv.Atoi(s)
		return i
	}

	doubler := func(i int) int {
		return i * 2
	}

	fmt.Printf("%v\n", lazy.ToSlice(
		lazy.Map[int, int](doubler, // =>  [2 4 6 8]
			lazy.Map[string, int](atoi, // => [1 2 3 4]
				numbersAsStrings)))) // => ["1" "2" "3" "4"]

}
Output:

[2 4 6 8]

func Pairwise

func Pairwise[X any, Y [2]X](
	it It[X],
) It[Y]

Pairwise returns an iterator that produces overlapping pairs of values produced by the input. The input iterator should not be used anywhere else once provided to this function.

For example, for an iterator abc that produces the runes 'a', 'b', 'c', Pairwise(abc) produces the pairs [2]rune{'a', b'} and [2]rune{'b', 'c'}.

func PairwiseEnd

func PairwiseEnd[X any, Y [2]X](
	lastValue X,
	g It[X],
) It[Y]

PairwiseEnd is like Pairwise, however a final result pair [2]X{value, lastValue} is produced for the last value.

For example, for an iterator abc that produces the runes 'a', 'b', 'c', PairwiseFill(0, abc) produces the pairs [2]rune{'a', b'}, [2]rune{'b', 'c'} and [2]rune{'c', 0}.

func Repeat

func Repeat[X any](
	n int,
	x X,
) It[X]

Repeat produces the value x, with n repetitions. If n is negative, it produces x with infinite repetitions.

For example, Repeat(3, "foo") produces the values "foo", "foo", "foo".

func Take

func Take[X any](
	n int,
	xs It[X],
) It[X]

Take returns an iterator that produces only (up to) the first n items of the input iterator.

func Tee

func Tee[X any](
	n int,
	g It[X],
) []It[X]

Tee returns a slice of n iterators that each, individually, produce the same values otherwise produced by the input iterator. This can be thought of at "copying" an iterator. The input iterators should not be used anywhere else once provided to this function.

For example, given an iterator abc that produces the letters "a", "b", "c", Tee will return n iterators. Each returned iterator will, independently, produce the letters "a", "b", "c".

Where the returned iterators produces their inputs at different speeds (their consumers are "out of step"), this requires growing amounts of auxiliary storage.

func Values added in v2.8.5

func Values[X comparable, Y any](xs It[Pair[X, Y]]) It[Y]

func Zip

func Zip[X any, Y []X](
	its ...It[X],
) It[Y]

Zip returns an iterator that produces slices of the results of each input iterator, in lockstep, terminating when any input is exhausted. The input iterators should not be used anywhere else once provided to this function.

For example, for an iterator abc that produces the runes 'a', 'b', 'c', and an input iterator wxyz that produces the runes 'w', 'x', 'y', 'z', Zip(abc, wxyz) produces the values []rune{'a', 'w'}, []rune{'b', 'x'}, []rune{'c', 'y'} before becoming exhausted.

If zipping multiple different types together, you will need to use iterators of type It[any].

func ZipFlat

func ZipFlat[X any](
	its ...It[X],
) It[X]

ZipFlat returns an iterator that produces the results of each input iterator, in turn, terminating when any input is exhausted. The input iterators should not be used anywhere else once provided to this function.

For example, for an iterator abc that produces the runes 'a', 'b', 'c', and an input iterator wxyz that produces the runes 'w', 'x', 'y', 'z', ZipFlat(abc, wxyz) produces the runes 'a', 'w', 'b', 'x', 'c', 'y' before becoming exhausted.

If zipping multiple different types together, you will need to use iterators of type It[any].

Some libraries call this function "round-robin" instead.

func (It[X]) Next

func (it It[X]) Next() (X, bool)

Next implements the proposed Iter interface.

type Joiner

type Joiner[In any, Out any] interface {
	Join(x In, isFinal bool)
	End() Out
}

Joiner is a type for something that can build a result by walking over an iterator using Join. It must be possible for a Joiner to be used multiple times (although not concurrently) from multiple calls to Join.

func StringJoiner

func StringJoiner(sep string) Joiner[string, string]

StringJoiner returns a Joiner for concatenating strings with a (possibly empty) separator.

type Pair added in v2.3.0

type Pair[K comparable, V any] struct {
	Key   K
	Value V
}

Pair is any Key, Value pair. Type K is any type that would be suitable as a KeyType in a map collection.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL