don't use this

master
Jordan Orelli 3 years ago
parent 9b9c986876
commit 1d343f3b22

@ -80,3 +80,24 @@ func Max[T constraints.Ordered](src Able[T]) T {
} }
return max return max
} }
// Discarded Iterator types:
//
// This was my first attempt. I like to have a method that returns a bool so
// you can use it succinctly in a for loop, but I didn't love having to call
// both done and next
//
// type Ator[T any] interface {
// Done() bool
// Next() T
// }
//
// I was never optimistic about this. In practice, using this in a for loop is
// just annoying so I stopped doing it. But also there's another thing I find
// annoying: it's a copy every time. You're not really iterating over the
// values, you're iterating over copies of the values, it seemed like a lot of
// unecessary copying.
//
// type Ator[T any] interface {
// Next() (T, bool)
// }

@ -0,0 +1,26 @@
package iter
import "constraints"
type slice[T any] []T
type sliceIter[T any] struct {
s slice[T]
i int
}
func (it *sliceIter[T]) Next(v *T) bool {
if it.i >= len(it.s) {
return false
}
*v = it.s[it.i]
it.i++
return true
}
func (it *sliceIter[T]) Iter() Ator[T] { return &sliceIter[T]{s: it.s} }
func (s slice[T]) Iter() Ator[T] { return &sliceIter[T]{s: s} }
// Slice takes a slice and returns an iterable backed by that slice
func Slice[T constraints.Integer](s []T) Able[T] { return slice[T](s) }

@ -1,12 +1,12 @@
package list package list
type Iterable[T any] interface { // type Iterable[T any] interface {
Iter() Iter[T] // Iter() Iter[T]
} // }
//
type Iter[T any] interface { // type Iter[T any] interface {
Next(*T) bool // Next(*T) bool
} // }
// for v, it := iter.Ate(foods); it.Next(&v); { // for v, it := iter.Ate(foods); it.Next(&v); {
// //
@ -34,7 +34,6 @@ type Iter[T any] interface {
// return max // return max
// } // }
// Discarded Iterator types: // Discarded Iterator types:
// //
// This was my first attempt. I like to have a method that returns a bool so // This was my first attempt. I like to have a method that returns a bool so

@ -5,6 +5,8 @@ import (
"sync" "sync"
"constraints" "constraints"
"fmt" "fmt"
"github.com/jordanorelli/generic/iter"
) )
type node[T any] struct { type node[T any] struct {
@ -41,9 +43,6 @@ func Make[T any](vals ...T) List[T] {
return l return l
} }
// func From[T any](it iter.Able) List[T] {
// }
// Empty is true for empty lists // Empty is true for empty lists
func (l List[T]) Empty() bool { func (l List[T]) Empty() bool {
return l.head == nil return l.head == nil
@ -93,7 +92,9 @@ func (l List[T]) Head() T {
// Tail returns a list which is the original list without its Head element. // Tail returns a list which is the original list without its Head element.
// If the original list is an empty list or a list of size 1, Tail is an // If the original list is an empty list or a list of size 1, Tail is an
// empty list. // empty list. Note that Tail creates a new list that is backed by the same
// elements as the old list; mutations on the origin list are visible in the
// tail and vice-versa.
func (l List[T]) Tail() List[T] { func (l List[T]) Tail() List[T] {
if l.head == nil || l.head.next == nil { if l.head == nil || l.head.next == nil {
return List[T]{} return List[T]{}
@ -114,13 +115,13 @@ func (l List[T]) Len() int {
return i return i
} }
type iter[T any] struct { type _iter[T any] struct {
n *node[T] n *node[T]
} }
func (i iter[T]) Done() bool { return i.n == nil } func (i _iter[T]) Done() bool { return i.n == nil }
func (i *iter[T]) Next(dest *T) bool { func (i *_iter[T]) Next(dest *T) bool {
if i.n == nil { if i.n == nil {
return false return false
} }
@ -130,9 +131,9 @@ func (i *iter[T]) Next(dest *T) bool {
return true return true
} }
func (l List[T]) Iter() Iter[T] { func (i *_iter[T]) Iter() iter.Ator[T] { return &_iter[T]{n: i.n} }
return &iter[T]{n: l.head}
} func (l List[T]) Iter() iter.Ator[T] { return &_iter[T]{n: l.head} }
func Max[T constraints.Ordered](l List[T]) T { func Max[T constraints.Ordered](l List[T]) T {
if l.Empty() { if l.Empty() {
@ -149,45 +150,81 @@ func Max[T constraints.Ordered](l List[T]) T {
return v return v
} }
// Map exists as a method to permit chaining in the event that your input
// function maps T -> T. Since methods cannot have type parameters, mapping a
// function that transforms T -> Z is not possible as a method.
func (l List[T]) Map(f func(T) T) List[T] { return Map(l, f) }
// Map applies the input function f to each element of the list l, returning a // Map applies the input function f to each element of the list l, returning a
// new list containing the values produced by f // new list containing the values produced by f
func (l List[T]) Map(f func(T) T) List[T] { func Map[T any, Z any](l List[T], f func(T) Z) List[Z] {
if l.Empty() { if l.Empty() {
return List[T]{} var empty List[Z]
return empty
} }
mapped := List[T]{head: &node[T]{val: f(l.head.val)}} mapped := List[Z]{head: &node[Z]{val: f(l.head.val)}}
last := mapped.head last := mapped.head
for n := l.head.next; n != nil; n = n.next { for n := l.head.next; n != nil; n = n.next {
last.next = &node[T]{val: f(n.val)} last.next = &node[Z]{val: f(n.val)}
last = last.next last = last.next
} }
return mapped return mapped
} }
type numbered[T any] struct {
val T
i int
}
func waitNClose[T any](wg *sync.WaitGroup, c chan T) {
wg.Wait()
close(c)
}
// Run is the same as Map, but is run concurrently. The function f will be run // Run is the same as Map, but is run concurrently. The function f will be run
// for every element of l in its own goroutine. // for every element of l in its own goroutine. The results of running f on
// each of the inputs will be stored into a new list in an order-preserving
// manner.
func Run[T any, Z any](l List[T], f func(T) Z) List[Z] { func Run[T any, Z any](l List[T], f func(T) Z) List[Z] {
if l.Empty() {
var empty List[Z]
return empty
}
// surprise: type declarations are not allowed inside of generic functions
//
// type numbered[T any] struct {
// val T
// i int
// }
var wg sync.WaitGroup var wg sync.WaitGroup
c := make(chan Z) c := make(chan numbered[Z])
for n := l.head; n != nil; n = n.next { i := 0
for n := l.head; n != nil; n = n.next{
wg.Add(1) wg.Add(1)
go func(v T) { go func(v T, i int) {
defer wg.Done() defer wg.Done()
c <- f(v) c <- numbered[Z]{val: f(v), i: i}
}(n.val) }(n.val, i)
i++
} }
go func() { mem := make([]Z, i)
wg.Wait() go waitNClose(&wg, c)
close(c) for z := range c {
}() mem[z.i] = z.val
}
var results List[Z] var results List[Z]
for z := range c { for i, _ := range mem {
results.Push(z) results.head = &node[Z]{
val: mem[i],
next: results.head,
}
} }
return results return results
} }
@ -218,3 +255,23 @@ func (l List[T]) Filter(f func(T) bool) List[T] {
return passed return passed
} }
type Pair[T any, Z any] struct {
Left T
Right Z
}
// Zip takes two lists and joins them to create a list of pairs. It's the same
// as the python zip function, and totally stupid and Pair should not be in
// this package but I'm testing the iterable interfaces and this shows they are
// good, actually
func Zip[T any, Z any](left List[T], right List[Z]) List[Pair[T, Z]] {
lit, rit := left.Iter(), right.Iter()
var out List[Pair[T, Z]]
var next Pair[T, Z]
for lit.Next(&next.Left) && rit.Next(&next.Right) {
out.head = &node[Pair[T, Z]]{val: next, next: out.head}
}
return out
}

@ -2,6 +2,7 @@ package list
import ( import (
"testing" "testing"
"time"
) )
func TestEmpty(t *testing.T) { func TestEmpty(t *testing.T) {
@ -124,7 +125,18 @@ func TestMap(t *testing.T) {
eq(t, 0, nums.At(3)) eq(t, 0, nums.At(3))
eq(t, 30, Max(nums)) eq(t, 30, Max(nums))
}
func TestRun(t *testing.T) {
sleep := func(n int) time.Duration {
dur := time.Duration(n) * time.Millisecond
time.Sleep(dur)
return dur
}
l := Make(1, 2, 3, 4, 5, 4, 3, 2, 1)
durs := Run(l, sleep)
t.Logf("%v", durs)
} }
func TestIter(t *testing.T) { func TestIter(t *testing.T) {

@ -6,6 +6,7 @@ import (
"github.com/jordanorelli/generic/iter" "github.com/jordanorelli/generic/iter"
) )
// Span represents some range of integers
type Span[T constraints.Integer] struct { type Span[T constraints.Integer] struct {
Start T Start T
End T End T

@ -24,7 +24,7 @@ func TestSpan(t *testing.T) {
// can be inferred, but when New returns a value of Span[T] (which // can be inferred, but when New returns a value of Span[T] (which
// satisfies iter.Able[T]), the type parameter cannot be inferred. I don't // satisfies iter.Able[T]), the type parameter cannot be inferred. I don't
// know why this behavior exists or if this is the intended behavior. // know why this behavior exists or if this is the intended behavior.
// + // |
// | // |
// V // V
beer := iter.Max[int](New(1, 100)) beer := iter.Max[int](New(1, 100))
@ -37,6 +37,20 @@ func TestSpan(t *testing.T) {
t.Errorf("expected 30 to be old but saw %d instead", old) t.Errorf("expected 30 to be old but saw %d instead", old)
} }
// honestly a very ridiculous use-case, but I'm looking at some weird
// inferrence corner cases. It seems the third parameter can't be the
// literal 2, you have to explicitly type it.
alpha := Step('a', 'z'+1, rune(2))
// the rune type can't be inferred here either
for a, it := iter.Start[rune](alpha); it.Next(&a); {
t.Logf("%c", a)
}
// the rune type can't be inferred here either!
for a, it := iter.Start[rune](alpha.Iter()); it.Next(&a); {
}
t.Logf("%T", iter.Max[int8](New[int8](3, 10))) t.Logf("%T", iter.Max[int8](New[int8](3, 10)))
} }

Loading…
Cancel
Save