289 lines
7.0 KiB
Go
289 lines
7.0 KiB
Go
// This package describes ginger datastructures, and many of the operations
|
|
// which can be used on those data structures
|
|
package seq
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
|
|
"github.com/mediocregopher/ginger/types"
|
|
)
|
|
|
|
// The general interface which most operations will actually operate on. Acts as
|
|
// an interface onto any data structure
|
|
type Seq interface {
|
|
|
|
// Returns the "first" element in the data structure as well as a Seq
|
|
// containing a copy of the rest of the elements in the data structure. The
|
|
// "first" element can be random for structures which don't have a concept
|
|
// of order (like Set). Calling FirstRest on an empty Seq (Size() == 0) will
|
|
// return "first" as nil, the same empty Seq , and false. The third return
|
|
// value is true in all other cases.
|
|
FirstRest() (types.Elem, Seq, bool)
|
|
}
|
|
|
|
// Returns the number of elements contained in the data structure. In general
|
|
// this completes in O(N) time, except for Set and HashMap for which it
|
|
// completes in O(1)
|
|
func Size(s Seq) uint64 {
|
|
switch st := s.(type) {
|
|
case *Set:
|
|
return st.Size()
|
|
case *HashMap:
|
|
return st.Size()
|
|
default:
|
|
}
|
|
|
|
var ok bool
|
|
for i := uint64(0); ; {
|
|
if _, s, ok = s.FirstRest(); ok {
|
|
i++
|
|
} else {
|
|
return i
|
|
}
|
|
}
|
|
}
|
|
|
|
// Returns whether or not the given Seq is empty. This is accomplished using
|
|
// FirstRest and NOT just by naively returning Size(s) == 0.
|
|
func Empty(s Seq) bool {
|
|
_, _, ok := s.FirstRest()
|
|
return !ok
|
|
}
|
|
|
|
// Returns the elements in the Seq as a slice. If the underlying Seq has any
|
|
// implicit order to it that order will be kept. An empty Seq will return an
|
|
// empty slice; nil is never returned. In general this completes in O(N) time.
|
|
func ToSlice(s Seq) []types.Elem {
|
|
var el types.Elem
|
|
var ok bool
|
|
for ret := make([]types.Elem, 0, 8); ; {
|
|
if el, s, ok = s.FirstRest(); ok {
|
|
ret = append(ret, el)
|
|
} else {
|
|
return ret
|
|
}
|
|
}
|
|
}
|
|
|
|
// Turns a Seq into a string, with each element separated by a space and with a
|
|
// dstart and dend wrapping the whole thing
|
|
func ToString(s Seq, dstart, dend string) string {
|
|
buf := bytes.NewBufferString(dstart)
|
|
buf.WriteString(" ")
|
|
var el types.Elem
|
|
var strel fmt.Stringer
|
|
var rest Seq
|
|
var ok bool
|
|
for {
|
|
if el, rest, ok = s.FirstRest(); ok {
|
|
if strel, ok = el.(fmt.Stringer); ok {
|
|
buf.WriteString(strel.String())
|
|
} else {
|
|
buf.WriteString(fmt.Sprintf("%v", el))
|
|
}
|
|
buf.WriteString(" ")
|
|
s = rest
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
buf.WriteString(dend)
|
|
return buf.String()
|
|
}
|
|
|
|
// Returns a reversed copy of the List. Completes in O(N) time.
|
|
func Reverse(s Seq) Seq {
|
|
l := NewList()
|
|
var el types.Elem
|
|
var ok bool
|
|
for {
|
|
if el, s, ok = s.FirstRest(); ok {
|
|
l = l.Prepend(el)
|
|
} else {
|
|
return l
|
|
}
|
|
}
|
|
}
|
|
|
|
// Returns a Seq consisting of the result of applying fn to each element in the
|
|
// given Seq. Completes in O(N) time.
|
|
func Map(fn func(types.Elem) types.Elem, s Seq) Seq {
|
|
l := NewList()
|
|
var el types.Elem
|
|
var ok bool
|
|
for {
|
|
if el, s, ok = s.FirstRest(); ok {
|
|
l = l.Prepend(fn(el))
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
return Reverse(l)
|
|
}
|
|
|
|
// A function used in a reduce. The first argument is the accumulator, the
|
|
// second is an element from the Seq being reduced over. The ReduceFn returns
|
|
// the accumulator to be used in the next iteration, wherein that new
|
|
// accumulator will be called alongside the next element in the Seq. ReduceFn
|
|
// also returns a boolean representing whether or not the reduction should stop
|
|
// at this step. If true, the reductions will stop and any remaining elements in
|
|
// the Seq will be ignored.
|
|
type ReduceFn func(acc, el types.Elem) (types.Elem, bool)
|
|
|
|
// Reduces over the given Seq using ReduceFn, with acc as the first accumulator
|
|
// value in the reduce. See ReduceFn for more details on how it works. The
|
|
// return value is the result of the reduction. Completes in O(N) time.
|
|
func Reduce(fn ReduceFn, acc types.Elem, s Seq) types.Elem {
|
|
var el types.Elem
|
|
var ok, stop bool
|
|
for {
|
|
if el, s, ok = s.FirstRest(); ok {
|
|
acc, stop = fn(acc, el)
|
|
if stop {
|
|
break
|
|
}
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
return acc
|
|
}
|
|
|
|
// Returns the first element in Seq for which fn returns true, or nil. The
|
|
// returned boolean indicates whether or not a matching element was found.
|
|
// Completes in O(N) time.
|
|
func Any(fn func(el types.Elem) bool, s Seq) (types.Elem, bool) {
|
|
var el types.Elem
|
|
var ok bool
|
|
for {
|
|
if el, s, ok = s.FirstRest(); ok {
|
|
if fn(el) {
|
|
return el, true
|
|
}
|
|
} else {
|
|
return nil, false
|
|
}
|
|
}
|
|
}
|
|
|
|
// Returns true if fn returns true for all elements in the Seq. Completes in
|
|
// O(N) time.
|
|
func All(fn func(types.Elem) bool, s Seq) bool {
|
|
var el types.Elem
|
|
var ok bool
|
|
for {
|
|
if el, s, ok = s.FirstRest(); ok {
|
|
if !fn(el) {
|
|
return false
|
|
}
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
// Returns a Seq containing all elements in the given Seq for which fn returned
|
|
// true. Completes in O(N) time.
|
|
func Filter(fn func(el types.Elem) bool, s Seq) Seq {
|
|
l := NewList()
|
|
var el types.Elem
|
|
var ok bool
|
|
for {
|
|
if el, s, ok = s.FirstRest(); ok {
|
|
if fn(el) {
|
|
l = l.Prepend(el)
|
|
}
|
|
} else {
|
|
return Reverse(l)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Flattens the given Seq into a single, one-dimensional Seq. This method only
|
|
// flattens Seqs found in the top level of the given Seq, it does not recurse
|
|
// down to multiple layers. Completes in O(N*M) time, where N is the number of
|
|
// elements in the Seq and M is how large the Seqs in those elements actually
|
|
// are.
|
|
func Flatten(s Seq) Seq {
|
|
l := NewList()
|
|
var el types.Elem
|
|
var ok bool
|
|
for {
|
|
if el, s, ok = s.FirstRest(); ok {
|
|
if els, ok := el.(Seq); ok {
|
|
l = l.PrependSeq(Reverse(els))
|
|
} else {
|
|
l = l.Prepend(el)
|
|
}
|
|
} else {
|
|
return Reverse(l)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Returns a Seq containing the first n elements in the given Seq. If n is
|
|
// greater than the length of the given Seq then the whole Seq is returned.
|
|
// Completes in O(N) time.
|
|
func Take(n uint64, s Seq) Seq {
|
|
l := NewList()
|
|
var el types.Elem
|
|
var ok bool
|
|
for i := uint64(0); i < n; i++ {
|
|
el, s, ok = s.FirstRest()
|
|
if !ok {
|
|
break
|
|
}
|
|
l = l.Prepend(el)
|
|
}
|
|
return Reverse(l)
|
|
}
|
|
|
|
// Goes through each item in the given Seq until an element returns false from
|
|
// pred. Returns a new Seq containing these truthful elements. Completes in O(N)
|
|
// time.
|
|
func TakeWhile(pred func(types.Elem) bool, s Seq) Seq {
|
|
l := NewList()
|
|
var el types.Elem
|
|
var ok bool
|
|
for {
|
|
el, s, ok = s.FirstRest()
|
|
if !ok || !pred(el) {
|
|
break
|
|
}
|
|
l = l.Prepend(el)
|
|
}
|
|
return Reverse(l)
|
|
}
|
|
|
|
// Returns a Seq the is the previous Seq without the first n elements. If n is
|
|
// greater than the length of the Seq, returns an empty Seq. Completes in O(N)
|
|
// time.
|
|
func Drop(n uint64, s Seq) Seq {
|
|
var ok bool
|
|
for i := uint64(0); i < n; i++ {
|
|
_, s, ok = s.FirstRest()
|
|
if !ok {
|
|
break
|
|
}
|
|
}
|
|
return s
|
|
}
|
|
|
|
// Drops elements from the given Seq until pred returns false for an element.
|
|
// Returns a Seq of the remaining elements (including the one which returned
|
|
// false). Completes in O(N) time.
|
|
func DropWhile(pred func(types.Elem) bool, s Seq) Seq {
|
|
var el types.Elem
|
|
var curs Seq
|
|
var ok bool
|
|
for {
|
|
el, curs, ok = s.FirstRest()
|
|
if !ok || !pred(el) {
|
|
break
|
|
}
|
|
s = curs
|
|
}
|
|
return s
|
|
}
|