master
Jordan Orelli 3 years ago
parent ff53acaa2e
commit 4032920454

@ -14,14 +14,37 @@ type Ator[T any] interface {
// returns false should continue to return false; once iteration is // returns false should continue to return false; once iteration is
// complete it should be complete forever. // complete it should be complete forever.
Next(*T) bool Next(*T) bool
// An iterator must also be iterable. That is, a valid iterator must be
// able to create a new iterator beginning at the same position of that
// iterator, such that calling Iter() on an Iterator returns a new
// Iterator, and that both can be iterated without affecting one another.
Able[T]
} }
func Ate[T any](a Able[T]) (T, Ator[T]) { // Start starts the iteration for an Iterable. This is a convenience function
// to facilitate iterating in a for loop. E.g., given an Iterable value l, such
// as a list, the following would iterate over the entire iterable:
//
// for v, it := iter.Start(l); it.Next(&v); {
// // utilize v here
// }
//
// Without such a construct, a developer would have to do something similar instead:
//
// for v, it := 0, l.Iter(); it.Next(&v); {
//
// }
//
// Doing that would require that the developer type out the zero-value for that
// type, when that value could just as easily be inferred.
func Start[T any](a Able[T]) (T, Ator[T]) {
var v T var v T
return v, a.Iter() return v, a.Iter()
} }
func Min[T constraints.Ordered](it Ator[T]) T { func Min[T constraints.Ordered](src Able[T]) T {
it := src.Iter()
var v T var v T
if !it.Next(&v) { if !it.Next(&v) {
var zero T var zero T
@ -36,7 +59,8 @@ func Min[T constraints.Ordered](it Ator[T]) T {
return min return min
} }
func Max[T constraints.Ordered](it Ator[T]) T { func Max[T constraints.Ordered](src Able[T]) T {
it := src.Iter()
var v T var v T
if !it.Next(&v) { if !it.Next(&v) {
var zero T var zero T

@ -5,13 +5,29 @@ import (
) )
type span[T constraints.Integer] struct { type span[T constraints.Integer] struct {
next T start T
final T end T
step T
}
type spanIter[T constraints.Integer] struct {
start T
end T
step T step T
next T
} }
func (s *span[T]) Next(n *T) bool { func (s span[T]) Iter() Ator[T] {
if s.next >= s.final { return &spanIter[T]{
start: s.start,
end: s.end,
step: s.step,
next: s.start,
}
}
func (s *spanIter[T]) Next(n *T) bool {
if s.next >= s.end {
return false return false
} }
*n = s.next *n = s.next
@ -19,12 +35,24 @@ func (s *span[T]) Next(n *T) bool {
return true return true
} }
// Span creates a span of integers between start and end func (s spanIter[T]) Iter() Ator[T] { return &s }
func Span[T constraints.Integer](start, end T) Ator[T] {
return &span[T]{next: start, final: end, step: 1} // Span creates a span of integers between start and end. The is analagous to
// the "range" function in Python, but since range already means something in
// Go, span is the chosen name to avoid confusion with Go's concept of range.
func Span[T constraints.Integer](start, end T) Able[T] {
return &span[T]{
start: start,
end: end,
step: 1,
}
} }
// Step is the same as span, but allows for step sizes greater than 1 // Step is the same as span, but allows for step sizes greater than 1
func Step[T constraints.Integer](start, end, step T) Ator[T] { func Step[T constraints.Integer](start, end, step T) Able[T] {
return &span[T]{next: start, final: end, step: step} return &span[T]{
start: start,
end: end,
step: step,
}
} }

@ -6,7 +6,7 @@ import (
func TestSpan(t *testing.T) { func TestSpan(t *testing.T) {
var n int var n int
s := Span(1, 10) s := Span(1, 10).Iter()
s.Next(&n) s.Next(&n)
if n != 1 { if n != 1 {
@ -27,10 +27,12 @@ func TestSpan(t *testing.T) {
if old != 30 { if old != 30 {
t.Errorf("expected 30 to be old but saw %d instead", old) t.Errorf("expected 30 to be old but saw %d instead", old)
} }
t.Logf("%T", Max(Span[uint8](3, 10)))
} }
func TestStep(t *testing.T) { func TestStep(t *testing.T) {
s := Step(1, 10, 3) s := Step(1, 10, 3).Iter()
for n := 0; s.Next(&n); { for n := 0; s.Next(&n); {
t.Log(n) t.Log(n)
} }

Loading…
Cancel
Save