ginger/seq/seq.go
2014-10-24 13:32:28 -04:00

313 lines
7.6 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
}
// Runs pred on each element in the sequence, descending recursively if it
// encounters another Seq (without calling pred on that Seq). This amounts to a
// depth-first traverse. If pred ever returns false the traverse will stop.
// Returns false if the Traverse was stopped by pred, true otherwise.
func Traverse(pred func(types.Elem) bool, s Seq) bool {
var el types.Elem
var ok bool
for {
el, s, ok = s.FirstRest()
if !ok {
return true
}
var predRet bool
if inners, ok := el.(Seq); ok {
predRet = Traverse(pred, inners)
} else {
predRet = pred(el)
}
if !predRet {
return false
}
}
}