You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

278 lines
5.7 KiB
Go

package list
import (
"strings"
"sync"
"constraints"
"fmt"
"github.com/jordanorelli/generic/iter"
)
type node[T any] struct {
val T
next *node[T]
}
// List[T] is a singly-linked list of T
type List[T any] struct {
head *node[T]
}
func (l List[T]) String() string {
var buf strings.Builder
buf.WriteRune('[')
for n := l.head; n != nil; n = n.next {
fmt.Fprintf(&buf, "%v", n.val)
if n.next != nil {
buf.WriteString(", ")
}
}
buf.WriteRune(']')
return buf.String()
}
// Make creates a list of T with a set of provided values. It's called Make
// instead of New because it performs O(n) allocations, where n == len(vals)
func Make[T any](vals ...T) List[T] {
var l List[T]
for i := len(vals)-1; i >= 0; i-- {
l.Push(vals[i])
}
return l
}
// Empty is true for empty lists
func (l List[T]) Empty() bool {
return l.head == nil
}
// At treats the list like an array and gets the value at the i'th position in
// the list (zero-indexed)
func (l List[T]) At(i int) T {
for n, at := l.head, 0; n != nil; n, at = n.next, at+1 {
if at == i {
return n.val
}
}
var v T
return v
}
// Push adds an element to the front of the list
func (l *List[T]) Push(v T) {
l.head = &node[T]{
val: v,
next: l.head,
}
}
// Pop returns the first element of the list and removes it from the list.
func (l *List[T]) Pop() T {
if l.Empty() {
var v T
return v
}
v := l.head.val
l.head = l.head.next
return v
}
// Head returns the first element of the list. If the list is empty, Head
// returns the zero-value for the type T. This is the same thing as Peek()
func (l List[T]) Head() T {
if l.head == nil {
var v T
return v
}
return l.head.val
}
// 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
// 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] {
if l.head == nil || l.head.next == nil {
return List[T]{}
}
return List[T]{head: l.head.next}
}
// Len is the length of the list
func (l List[T]) Len() int {
if l.head == nil {
return 0
}
i := 0
for n := l.head; n != nil; n = n.next {
i++
}
return i
}
type _iter[T any] struct {
n *node[T]
}
func (i _iter[T]) Done() bool { return i.n == nil }
func (i *_iter[T]) Next(dest *T) bool {
if i.n == nil {
return false
}
*dest = i.n.val
i.n = i.n.next
return true
}
func (i *_iter[T]) Iter() iter.Ator[T] { return &_iter[T]{n: i.n} }
func (l List[T]) Iter() iter.Ator[T] { return &_iter[T]{n: l.head} }
func Max[T constraints.Ordered](l List[T]) T {
if l.Empty() {
var v T
return v
}
v := l.head.val
for n := l.head.next; n != nil; n = n.next {
if n.val > v {
v = n.val
}
}
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
// new list containing the values produced by f
func Map[T any, Z any](l List[T], f func(T) Z) List[Z] {
if l.Empty() {
var empty List[Z]
return empty
}
mapped := List[Z]{head: &node[Z]{val: f(l.head.val)}}
last := mapped.head
for n := l.head.next; n != nil; n = n.next {
last.next = &node[Z]{val: f(n.val)}
last = last.next
}
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
// 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] {
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
c := make(chan numbered[Z])
i := 0
for n := l.head; n != nil; n = n.next{
wg.Add(1)
go func(v T, i int) {
defer wg.Done()
c <- numbered[Z]{val: f(v), i: i}
}(n.val, i)
i++
}
mem := make([]Z, i)
go waitNClose(&wg, c)
for z := range c {
mem[z.i] = z.val
}
var results List[Z]
for i, _ := range mem {
results.head = &node[Z]{
val: mem[i],
next: results.head,
}
}
return results
}
// Filter applies a predicate function f to each element of the list and
// returns a new list containing the values of the elements that passed the
// predicate
func (l List[T]) Filter(f func(T) bool) List[T] {
if l.Empty() {
return List[T]{}
}
var passed List[T]
var last *node[T]
for n := l.head; n != nil; n = n.next {
if !f(n.val) {
continue
}
if passed.Empty() {
passed.head = &node[T]{val: n.val}
last = passed.head
} else {
last.next = &node[T]{val: n.val}
last = last.next
}
}
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
}