-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8483 from ProofOfKeags/feature/for-loop-destroyer
fn: add slice utilities
- Loading branch information
Showing
4 changed files
with
328 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,21 @@ | ||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/lightninglabs/neutrino/cache v1.1.2 h1:C9DY/DAPaPxbFC+xNNEI/z1SJY9GS3shmlu5hIQ798g= | ||
github.com/lightninglabs/neutrino/cache v1.1.2/go.mod h1:XJNcgdOw1LQnanGjw8Vj44CvguYA25IMKjWFZczwZuo= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= | ||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= | ||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= | ||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= | ||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= | ||
golang.org/x/exp v0.0.0-20231226003508-02704c960a9b h1:kLiC65FbiHWFAOu+lxwNPujcsl8VYyTYYEZnsOO1WK4= | ||
golang.org/x/exp v0.0.0-20231226003508-02704c960a9b/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
package fn | ||
|
||
// All returns true when the supplied predicate evaluates to true for all of | ||
// the values in the slice. | ||
func All[A any](pred func(A) bool, s []A) bool { | ||
for _, val := range s { | ||
if !pred(val) { | ||
return false | ||
} | ||
} | ||
|
||
return true | ||
} | ||
|
||
// Any returns true when the supplied predicate evaluates to true for any of | ||
// the values in the slice. | ||
func Any[A any](pred func(A) bool, s []A) bool { | ||
for _, val := range s { | ||
if pred(val) { | ||
return true | ||
} | ||
} | ||
|
||
return false | ||
} | ||
|
||
// Map applies the function argument to all members of the slice and returns a | ||
// slice of those return values. | ||
func Map[A, B any](f func(A) B, s []A) []B { | ||
res := make([]B, 0, len(s)) | ||
|
||
for _, val := range s { | ||
res = append(res, f(val)) | ||
} | ||
|
||
return res | ||
} | ||
|
||
// Filter creates a new slice of values where all the members of the returned | ||
// slice pass the predicate that is supplied in the argument. | ||
func Filter[A any](pred func(A) bool, s []A) []A { | ||
res := make([]A, 0) | ||
|
||
for _, val := range s { | ||
if pred(val) { | ||
res = append(res, val) | ||
} | ||
} | ||
|
||
return res | ||
} | ||
|
||
// Foldl iterates through all members of the slice left to right and reduces | ||
// them pairwise with an accumulator value that is seeded with the seed value in | ||
// the argument. | ||
func Foldl[A, B any](f func(B, A) B, seed B, s []A) B { | ||
acc := seed | ||
|
||
for _, val := range s { | ||
acc = f(acc, val) | ||
} | ||
|
||
return acc | ||
} | ||
|
||
// Foldr, is exactly like Foldl except that it iterates over the slice from | ||
// right to left. | ||
func Foldr[A, B any](f func(A, B) B, seed B, s []A) B { | ||
acc := seed | ||
|
||
for i := range s { | ||
acc = f(s[len(s)-1-i], acc) | ||
} | ||
|
||
return acc | ||
} | ||
|
||
// Find returns the first value that passes the supplied predicate, or None if | ||
// the value wasn't found. | ||
func Find[A any](pred func(A) bool, s []A) Option[A] { | ||
for _, val := range s { | ||
if pred(val) { | ||
return Some(val) | ||
} | ||
} | ||
|
||
return None[A]() | ||
} | ||
|
||
// Flatten takes a slice of slices and returns a concatenation of those slices. | ||
func Flatten[A any](s [][]A) []A { | ||
sz := Foldr( | ||
func(l []A, acc uint64) uint64 { | ||
return uint64(len(l)) + acc | ||
}, 0, s, | ||
) | ||
|
||
res := make([]A, 0, sz) | ||
|
||
for _, val := range s { | ||
res = append(res, val...) | ||
} | ||
|
||
return res | ||
} | ||
|
||
// Replicate generates a slice of values initialized by the prototype value. | ||
func Replicate[A any](n uint, val A) []A { | ||
res := make([]A, n) | ||
|
||
for i := range res { | ||
res[i] = val | ||
} | ||
|
||
return res | ||
} | ||
|
||
// Span, applied to a predicate and a slice, returns two slices where the first | ||
// element is the longest prefix (possibly empty) of slice elements that | ||
// satisfy the predicate and second element is the remainder of the slice. | ||
func Span[A any](pred func(A) bool, s []A) ([]A, []A) { | ||
for i := range s { | ||
if !pred(s[i]) { | ||
fst := make([]A, i) | ||
snd := make([]A, len(s)-i) | ||
|
||
copy(fst, s[:i]) | ||
copy(snd, s[i:]) | ||
|
||
return fst, snd | ||
} | ||
} | ||
|
||
res := make([]A, len(s)) | ||
copy(res, s) | ||
|
||
return res, []A{} | ||
} | ||
|
||
// SplitAt(n, s) returns a tuple where first element is s prefix of length n | ||
// and second element is the remainder of the list. | ||
func SplitAt[A any](n uint, s []A) ([]A, []A) { | ||
fst := make([]A, n) | ||
snd := make([]A, len(s)-int(n)) | ||
|
||
copy(fst, s[:n]) | ||
copy(snd, s[n:]) | ||
|
||
return fst, snd | ||
} | ||
|
||
// ZipWith combines slice elements with the same index using the function | ||
// argument, returning a slice of the results. | ||
func ZipWith[A, B, C any](f func(A, B) C, a []A, b []B) []C { | ||
var l uint | ||
|
||
if la, lb := len(a), len(b); la < lb { | ||
l = uint(la) | ||
} else { | ||
l = uint(lb) | ||
} | ||
|
||
res := make([]C, l) | ||
|
||
for i := 0; i < int(l); i++ { | ||
res[i] = f(a[i], b[i]) | ||
} | ||
|
||
return res | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
package fn | ||
|
||
import ( | ||
"slices" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func even(a int) bool { return a%2 == 0 } | ||
func odd(a int) bool { return a%2 != 0 } | ||
|
||
func TestAll(t *testing.T) { | ||
x := []int{0, 2, 4, 6, 8} | ||
require.True(t, All(even, x)) | ||
require.False(t, All(odd, x)) | ||
|
||
y := []int{1, 3, 5, 7, 9} | ||
require.False(t, All(even, y)) | ||
require.True(t, All(odd, y)) | ||
|
||
z := []int{0, 2, 4, 6, 9} | ||
require.False(t, All(even, z)) | ||
require.False(t, All(odd, z)) | ||
} | ||
|
||
func TestAny(t *testing.T) { | ||
x := []int{1, 3, 5, 7, 9} | ||
require.False(t, Any(even, x)) | ||
require.True(t, Any(odd, x)) | ||
|
||
y := []int{0, 3, 5, 7, 9} | ||
require.True(t, Any(even, y)) | ||
require.True(t, Any(odd, y)) | ||
|
||
z := []int{0, 2, 4, 6, 8} | ||
require.True(t, Any(even, z)) | ||
require.False(t, Any(odd, z)) | ||
} | ||
|
||
func TestMap(t *testing.T) { | ||
inc := func(i int) int { return i + 1 } | ||
|
||
x := []int{0, 2, 4, 6, 8} | ||
|
||
y := Map(inc, x) | ||
|
||
z := []int{1, 3, 5, 7, 9} | ||
|
||
require.True(t, slices.Equal(y, z)) | ||
} | ||
|
||
func TestFilter(t *testing.T) { | ||
x := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} | ||
|
||
y := Filter(even, x) | ||
|
||
require.True(t, All(even, y)) | ||
|
||
z := Filter(odd, y) | ||
|
||
require.Zero(t, len(z)) | ||
} | ||
|
||
func TestFoldl(t *testing.T) { | ||
seed := []int{} | ||
stupid := func(s []int, a int) []int { return append(s, a) } | ||
|
||
x := []int{0, 1, 2, 3, 4} | ||
|
||
r := Foldl(stupid, seed, x) | ||
|
||
require.True(t, slices.Equal(x, r)) | ||
} | ||
|
||
func TestFoldr(t *testing.T) { | ||
seed := []int{} | ||
stupid := func(a int, s []int) []int { return append(s, a) } | ||
|
||
x := []int{0, 1, 2, 3, 4} | ||
|
||
z := Foldr(stupid, seed, x) | ||
|
||
slices.Reverse[[]int](x) | ||
|
||
require.True(t, slices.Equal(x, z)) | ||
} | ||
|
||
func TestFind(t *testing.T) { | ||
x := []int{10, 11, 12, 13, 14, 15} | ||
|
||
div3 := func(a int) bool { return a%3 == 0 } | ||
div8 := func(a int) bool { return a%8 == 0 } | ||
|
||
require.Equal(t, Find(div3, x), Some(12)) | ||
|
||
require.Equal(t, Find(div8, x), None[int]()) | ||
} | ||
|
||
func TestFlatten(t *testing.T) { | ||
x := [][]int{{0}, {1}, {2}} | ||
|
||
y := Flatten(x) | ||
|
||
require.True(t, slices.Equal(y, []int{0, 1, 2})) | ||
} | ||
|
||
func TestReplicate(t *testing.T) { | ||
require.True(t, slices.Equal([]int{1, 1, 1, 1, 1}, Replicate(5, 1))) | ||
} | ||
|
||
func TestSpan(t *testing.T) { | ||
x := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} | ||
lt5 := func(a int) bool { return a < 5 } | ||
|
||
low, high := Span(lt5, x) | ||
|
||
require.True(t, slices.Equal(low, []int{0, 1, 2, 3, 4})) | ||
require.True(t, slices.Equal(high, []int{5, 6, 7, 8, 9})) | ||
} | ||
|
||
func TestSplitAt(t *testing.T) { | ||
x := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} | ||
fst, snd := SplitAt(5, x) | ||
|
||
require.True(t, slices.Equal(fst, []int{0, 1, 2, 3, 4})) | ||
require.True(t, slices.Equal(snd, []int{5, 6, 7, 8, 9})) | ||
} | ||
|
||
func TestZipWith(t *testing.T) { | ||
eq := func(a, b int) bool { return a == b } | ||
x := []int{0, 1, 2, 3, 4} | ||
y := Replicate(5, 1) | ||
z := ZipWith(eq, x, y) | ||
require.True(t, slices.Equal( | ||
z, []bool{false, true, false, false, false}, | ||
)) | ||
} |