303 lines
7.2 KiB
Go
303 lines
7.2 KiB
Go
package fun
|
|
|
|
import (
|
|
"reflect"
|
|
"runtime"
|
|
"sync"
|
|
|
|
"github.com/BurntSushi/ty"
|
|
)
|
|
|
|
// All has a parametric type:
|
|
//
|
|
// func All(p func(A) bool, xs []A) bool
|
|
//
|
|
// All returns `true` if and only if every element in `xs` satisfies `p`.
|
|
func All(f, xs interface{}) bool {
|
|
chk := ty.Check(
|
|
new(func(func(ty.A) bool, []ty.A) bool),
|
|
f, xs)
|
|
vf, vxs := chk.Args[0], chk.Args[1]
|
|
|
|
xsLen := vxs.Len()
|
|
for i := 0; i < xsLen; i++ {
|
|
if !call1(vf, vxs.Index(i)).Interface().(bool) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Exists has a parametric type:
|
|
//
|
|
// func Exists(p func(A) bool, xs []A) bool
|
|
//
|
|
// Exists returns `true` if and only if an element in `xs` satisfies `p`.
|
|
func Exists(f, xs interface{}) bool {
|
|
chk := ty.Check(
|
|
new(func(func(ty.A) bool, []ty.A) bool),
|
|
f, xs)
|
|
vf, vxs := chk.Args[0], chk.Args[1]
|
|
|
|
xsLen := vxs.Len()
|
|
for i := 0; i < xsLen; i++ {
|
|
if call1(vf, vxs.Index(i)).Interface().(bool) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// In has a parametric type:
|
|
//
|
|
// func In(needle A, haystack []A) bool
|
|
//
|
|
// In returns `true` if and only if `v` can be found in `xs`. The equality test
|
|
// used is Go's standard `==` equality and NOT deep equality.
|
|
//
|
|
// Note that this requires that `A` be a type that can be meaningfully compared.
|
|
func In(needle, haystack interface{}) bool {
|
|
chk := ty.Check(
|
|
new(func(ty.A, []ty.A) bool),
|
|
needle, haystack)
|
|
vhaystack := chk.Args[1]
|
|
|
|
length := vhaystack.Len()
|
|
for i := 0; i < length; i++ {
|
|
if vhaystack.Index(i).Interface() == needle {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Map has a parametric type:
|
|
//
|
|
// func Map(f func(A) B, xs []A) []B
|
|
//
|
|
// Map returns the list corresponding to the return value of applying
|
|
// `f` to each element in `xs`.
|
|
func Map(f, xs interface{}) interface{} {
|
|
chk := ty.Check(
|
|
new(func(func(ty.A) ty.B, []ty.A) []ty.B),
|
|
f, xs)
|
|
vf, vxs, tys := chk.Args[0], chk.Args[1], chk.Returns[0]
|
|
|
|
xsLen := vxs.Len()
|
|
vys := reflect.MakeSlice(tys, xsLen, xsLen)
|
|
for i := 0; i < xsLen; i++ {
|
|
vy := call1(vf, vxs.Index(i))
|
|
vys.Index(i).Set(vy)
|
|
}
|
|
return vys.Interface()
|
|
}
|
|
|
|
// Filter has a parametric type:
|
|
//
|
|
// func Filter(p func(A) bool, xs []A) []A
|
|
//
|
|
// Filter returns a new list only containing the elements of `xs` that satisfy
|
|
// the predicate `p`.
|
|
func Filter(p, xs interface{}) interface{} {
|
|
chk := ty.Check(
|
|
new(func(func(ty.A) bool, []ty.A) []ty.A),
|
|
p, xs)
|
|
vp, vxs, tys := chk.Args[0], chk.Args[1], chk.Returns[0]
|
|
|
|
xsLen := vxs.Len()
|
|
vys := reflect.MakeSlice(tys, 0, xsLen)
|
|
for i := 0; i < xsLen; i++ {
|
|
vx := vxs.Index(i)
|
|
if call1(vp, vx).Bool() {
|
|
vys = reflect.Append(vys, vx)
|
|
}
|
|
}
|
|
return vys.Interface()
|
|
}
|
|
|
|
// Foldl has a parametric type:
|
|
//
|
|
// func Foldl(f func(A, B) B, init B, xs []A) B
|
|
//
|
|
// Foldl reduces a list of A to a single element B using a left fold with
|
|
// an initial value `init`.
|
|
func Foldl(f, init, xs interface{}) interface{} {
|
|
chk := ty.Check(
|
|
new(func(func(ty.A, ty.B) ty.B, ty.B, []ty.A) ty.B),
|
|
f, init, xs)
|
|
vf, vinit, vxs, tb := chk.Args[0], chk.Args[1], chk.Args[2], chk.Returns[0]
|
|
|
|
xsLen := vxs.Len()
|
|
vb := zeroValue(tb)
|
|
vb.Set(vinit)
|
|
if xsLen == 0 {
|
|
return vb.Interface()
|
|
}
|
|
|
|
vb.Set(call1(vf, vxs.Index(0), vb))
|
|
for i := 1; i < xsLen; i++ {
|
|
vb.Set(call1(vf, vxs.Index(i), vb))
|
|
}
|
|
return vb.Interface()
|
|
}
|
|
|
|
// Foldr has a parametric type:
|
|
//
|
|
// func Foldr(f func(A, B) B, init B, xs []A) B
|
|
//
|
|
// Foldr reduces a list of A to a single element B using a right fold with
|
|
// an initial value `init`.
|
|
func Foldr(f, init, xs interface{}) interface{} {
|
|
chk := ty.Check(
|
|
new(func(func(ty.A, ty.B) ty.B, ty.B, []ty.A) ty.B),
|
|
f, init, xs)
|
|
vf, vinit, vxs, tb := chk.Args[0], chk.Args[1], chk.Args[2], chk.Returns[0]
|
|
|
|
xsLen := vxs.Len()
|
|
vb := zeroValue(tb)
|
|
vb.Set(vinit)
|
|
if xsLen == 0 {
|
|
return vb.Interface()
|
|
}
|
|
|
|
vb.Set(call1(vf, vxs.Index(xsLen-1), vb))
|
|
for i := xsLen - 2; i >= 0; i-- {
|
|
vb.Set(call1(vf, vxs.Index(i), vb))
|
|
}
|
|
return vb.Interface()
|
|
}
|
|
|
|
// Concat has a parametric type:
|
|
//
|
|
// func Concat(xs [][]A) []A
|
|
//
|
|
// Concat returns a new flattened list by appending all elements of `xs`.
|
|
func Concat(xs interface{}) interface{} {
|
|
chk := ty.Check(
|
|
new(func([][]ty.A) []ty.A),
|
|
xs)
|
|
vxs, tflat := chk.Args[0], chk.Returns[0]
|
|
|
|
xsLen := vxs.Len()
|
|
vflat := reflect.MakeSlice(tflat, 0, xsLen*3)
|
|
for i := 0; i < xsLen; i++ {
|
|
vflat = reflect.AppendSlice(vflat, vxs.Index(i))
|
|
}
|
|
return vflat.Interface()
|
|
}
|
|
|
|
// Reverse has a parametric type:
|
|
//
|
|
// func Reverse(xs []A) []A
|
|
//
|
|
// Reverse returns a new slice that is the reverse of `xs`.
|
|
func Reverse(xs interface{}) interface{} {
|
|
chk := ty.Check(
|
|
new(func([]ty.A) []ty.A),
|
|
xs)
|
|
vxs, tys := chk.Args[0], chk.Returns[0]
|
|
|
|
xsLen := vxs.Len()
|
|
vys := reflect.MakeSlice(tys, xsLen, xsLen)
|
|
for i := 0; i < xsLen; i++ {
|
|
vys.Index(i).Set(vxs.Index(xsLen - 1 - i))
|
|
}
|
|
return vys.Interface()
|
|
}
|
|
|
|
// Copy has a parametric type:
|
|
//
|
|
// func Copy(xs []A) []A
|
|
//
|
|
// Copy returns a copy of `xs` using Go's `copy` operation.
|
|
func Copy(xs interface{}) interface{} {
|
|
chk := ty.Check(
|
|
new(func([]ty.A) []ty.A),
|
|
xs)
|
|
vxs, tys := chk.Args[0], chk.Returns[0]
|
|
|
|
xsLen := vxs.Len()
|
|
vys := reflect.MakeSlice(tys, xsLen, xsLen)
|
|
reflect.Copy(vys, vxs)
|
|
return vys.Interface()
|
|
}
|
|
|
|
// ParMap has a parametric type:
|
|
//
|
|
// func ParMap(f func(A) B, xs []A) []B
|
|
//
|
|
// ParMap is just like Map, except it applies `f` to each element in `xs`
|
|
// concurrently using N worker goroutines (where N is the number of CPUs
|
|
// available reported by the Go runtime). If you want to control the number
|
|
// of goroutines spawned, use `ParMapN`.
|
|
//
|
|
// It is important that `f` not be a trivial operation, otherwise the overhead
|
|
// of executing it concurrently will result in worse performance than using
|
|
// a `Map`.
|
|
func ParMap(f, xs interface{}) interface{} {
|
|
n := runtime.NumCPU()
|
|
if n < 1 {
|
|
n = 1
|
|
}
|
|
return ParMapN(f, xs, n)
|
|
}
|
|
|
|
// ParMapN has a parametric type:
|
|
//
|
|
// func ParMapN(f func(A) B, xs []A, n int) []B
|
|
//
|
|
// ParMapN is just like Map, except it applies `f` to each element in `xs`
|
|
// concurrently using `n` worker goroutines.
|
|
//
|
|
// It is important that `f` not be a trivial operation, otherwise the overhead
|
|
// of executing it concurrently will result in worse performance than using
|
|
// a `Map`.
|
|
func ParMapN(f, xs interface{}, n int) interface{} {
|
|
chk := ty.Check(
|
|
new(func(func(ty.A) ty.B, []ty.A) []ty.B),
|
|
f, xs)
|
|
vf, vxs, tys := chk.Args[0], chk.Args[1], chk.Returns[0]
|
|
|
|
xsLen := vxs.Len()
|
|
ys := reflect.MakeSlice(tys, xsLen, xsLen)
|
|
|
|
if n < 1 {
|
|
n = 1
|
|
}
|
|
work := make(chan int, n)
|
|
wg := new(sync.WaitGroup)
|
|
for i := 0; i < n; i++ {
|
|
wg.Add(1)
|
|
go func() {
|
|
for j := range work {
|
|
// Good golly miss molly. Is `reflect.Value.Index`
|
|
// safe to access/set from multiple goroutines?
|
|
// XXX: If not, we'll need an extra wave of allocation to
|
|
// use real slices of `reflect.Value`.
|
|
ys.Index(j).Set(call1(vf, vxs.Index(j)))
|
|
}
|
|
wg.Done()
|
|
}()
|
|
}
|
|
for i := 0; i < xsLen; i++ {
|
|
work <- i
|
|
}
|
|
close(work)
|
|
wg.Wait()
|
|
return ys.Interface()
|
|
}
|
|
|
|
// Range generates a list of integers corresponding to every integer in
|
|
// the half-open interval [x, y).
|
|
//
|
|
// Range will panic if `end < start`.
|
|
func Range(start, end int) []int {
|
|
if end < start {
|
|
panic("range must have end greater than or equal to start")
|
|
}
|
|
r := make([]int, end-start)
|
|
for i := start; i < end; i++ {
|
|
r[i-start] = i
|
|
}
|
|
return r
|
|
}
|