diff --git a/.gitignore b/.gitignore index ee92db5..cf4c27e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,7 @@ __pycache__/ .mypy_cache/ - -# goland +.hypothesis/ .idea - -# vscode .vscode - -# linux temp files, .swp *~ - diff --git a/Taskfile.yaml b/Taskfile.yaml index f260b73..afd5b8d 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -12,6 +12,12 @@ tasks: cmds: - go test -count 1 -cover -timeout 1s ./... + docs: + sources: + - "*/*.go" + cmds: + - pkgsite -http localhost:1338 + all: desc: Run all tests and linters cmds: diff --git a/slices/examples_test.go b/slices/examples_test.go index 5001346..95767cf 100644 --- a/slices/examples_test.go +++ b/slices/examples_test.go @@ -1,12 +1,32 @@ package slices_test import ( + "errors" "fmt" "github.com/life4/genesis/channels" "github.com/life4/genesis/slices" ) +func ExampleAll() { + even := func(item int) bool { return item%2 == 0 } + result := slices.All([]int{2, 4, 6}, even) + fmt.Println(result) + result = slices.All([]int{2, 4, 5}, even) + fmt.Println(result) + + // Output: + // true + // false +} + +func ExampleAllAsync() { + even := func(item int) bool { return item%2 == 0 } + result := slices.AllAsync([]int{2, 4, 6}, 0, even) + fmt.Println(result) + // Output: true +} + func ExampleAny() { even := func(item int) bool { return item%2 == 0 } result := slices.Any([]int{1, 2, 3}, even) @@ -17,6 +37,32 @@ func ExampleAny() { // true // false } + +func ExampleAnyAsync() { + even := func(item int) bool { return item%2 == 0 } + result := slices.AnyAsync([]int{1, 2, 3}, 0, even) + fmt.Println(result) + // Output: true +} + +func ExampleChoice() { + result, _ := slices.Choice([]int{3, 4, 5, 6}, 13) + fmt.Println(result) + // Output: 3 +} + +func ExampleCopy() { + s1 := []int{3, 4} + s2 := slices.Copy(s1) + s1 = append(s1, 5) + s2 = append(s2, 6) + fmt.Println(s1) + fmt.Println(s2) + // Output: + // [3 4 5] + // [3 4 6] +} + func ExampleFindIndex() { type UserId int index := slices.FindIndex( @@ -26,17 +72,6 @@ func ExampleFindIndex() { fmt.Println(index) // Output: 2 } -func ExampleAll() { - even := func(item int) bool { return item%2 == 0 } - result := slices.All([]int{2, 4, 6}, even) - fmt.Println(result) - result = slices.All([]int{2, 4, 5}, even) - fmt.Println(result) - - // Output: - // true - // false -} func ExampleChunkBy() { s := []int{1, 3, 4, 6, 8, 9} @@ -53,6 +88,14 @@ func ExampleChunkEvery() { // Output: [[1 1 2] [3 5 8] [13]] } +func ExampleConcat() { + s1 := []int{3, 4, 5} + s2 := []int{6, 7, 8} + result := slices.Concat(s1, s2) + fmt.Println(result) + // Output: [3 4 5 6 7 8] +} + func ExampleContains() { s := []int{2, 4, 6, 8} result := slices.Contains(s, 4) @@ -96,6 +139,217 @@ func ExampleDedup() { // Output: [1 2 3 2 3 1] } +func ExampleDedupBy() { + s := []int{1, 2, -2, -3, 3, -3, 2, 3, 1, 1} + abs := func(x int) int { + if x < 0 { + return -x + } + return x + } + result := slices.DedupBy(s, abs) + fmt.Println(result) + // Output: [1 2 -3 2 3 1] +} + +func ExampleDelete() { + s := []int{3, 4, 5, 3, 4, 5} + result := slices.Delete(s, 4) + fmt.Println(result) + // Output: [3 5 3 4 5] +} + +func ExampleDeleteAt() { + s := []int{3, 4, 5, 3, 4, 5} + result, _ := slices.DeleteAt(s, 1, 3) + fmt.Println(result) + // Output: [3 5 4 5] +} + +func ExampleDeleteAll() { + s := []int{3, 4, 5, 3, 4, 5} + result := slices.DeleteAll(s, 3) + fmt.Println(result) + // Output: [4 5 4 5] +} + +func ExampleDropEvery() { + s := []int{3, 4, 5, 6, 7, 8} + result, _ := slices.DropEvery(s, 2, 0) + fmt.Println(result) + // Output: [4 6 8] +} + +func ExampleDropWhile() { + s := []int{2, 4, 6, 7, 8, 9, 10} + even := func(x int) bool { return x%2 == 0 } + result := slices.DropWhile(s, even) + fmt.Println(result) + // Output: [7 8 9 10] +} + +func ExampleEach() { + s := []int{4, 5, 6} + slices.Each(s, func(x int) { + fmt.Println(x * 2) + }) + // Output: + // 8 + // 10 + // 12 +} + +func ExampleEachAsync() { + s := []int{4, 5, 6} + sum := 0 + slices.EachAsync(s, 0, func(x int) { + sum += x + }) + fmt.Println(sum) + // Output: 15 +} + +func ExampleEachErr() { + s := []int{4, 5, 6, 7, 8} + err := slices.EachErr(s, func(x int) error { + if x == 6 { + return errors.New("six found") + } + fmt.Println(x * 2) + return nil + }) + fmt.Println(err) + // Output: + // 8 + // 10 + // six found +} + +func ExampleEndsWith() { + s := []int{3, 4, 5, 6, 7, 8} + result := slices.EndsWith(s, []int{7, 8}) + fmt.Println(result) + // Output: true +} + +func ExampleEqual() { + s1 := []int{3, 4, 5} + s2 := []int{3, 4, 5, 6} + result := slices.Equal(s1, s2) + fmt.Println(result) + // Output: false +} + +func ExampleEqualBy() { + s1 := []int{3, 4, -5} + s2 := []int{3, -4, 5} + absEq := func(a, b int) bool { + if a < 0 { + a = -a + } + if b < 0 { + b = -b + } + return a == b + } + result := slices.EqualBy(s1, s2, absEq) + fmt.Println(result) + // Output: true +} + +func ExampleFilter() { + s := []int{4, 5, 6, 7, 8, 10, 12, 13} + even := func(x int) bool { return x%2 == 0 } + result := slices.Filter(s, even) + fmt.Println(result) + // Output: [4 6 8 10 12] +} + +func ExampleFilterAsync() { + s := []int{4, 5, 6, 7, 8, 10, 12, 13} + even := func(x int) bool { return x%2 == 0 } + result := slices.FilterAsync(s, 0, even) + fmt.Println(result) + // Output: [4 6 8 10 12] +} + +func ExampleFind() { + s := []int{5, 7, 9, 4, 3, 6} + even := func(x int) bool { return x%2 == 0 } + result, _ := slices.Find(s, even) + fmt.Println(result) + // Output: 4 +} + +func ExampleGroupBy() { + s := []int{3, 4, 5, 13, 14, 15, 23, 33} + even := func(x int) int { return x % 10 } + result := slices.GroupBy(s, even) + fmt.Println(result) + // Output: map[3:[3 13 23 33] 4:[4 14] 5:[5 15]] +} + +func ExampleGrow() { + s := make([]int, 1, 4) + fmt.Printf("Before: len=%d, cap=%d\n", len(s), cap(s)) + r := slices.Grow(s, 5) + fmt.Printf("After: len=%d, cap=%d\n", len(r), cap(r)) + // Output: + // Before: len=1, cap=4 + // After: len=1, cap=8 +} + +func ExampleIndex() { + s := []int{3, 4, 5} + index, _ := slices.Index(s, 4) + fmt.Println(index) + // Output: 1 +} + +func ExampleIndexBy() { + s := []int{5, 7, 9, 8, 3, 6} + even := func(x int) bool { return x%2 == 0 } + result, _ := slices.IndexBy(s, even) + fmt.Println(result) + // Output: 3 +} + +func ExampleInsertAt() { + s := []int{3, 4, 5} + result, _ := slices.InsertAt(s, 1, 6) + fmt.Println(result) + // Output: [3 6 4 5] +} + +func ExampleIntersect2() { + s1 := []int{3, 4, 5, 5, 6, 6, 7} + s2 := []int{6, 5, 5, 4, 8} + result := slices.Intersect2(s1, s2) + fmt.Println(result) + // Output: [4 5 6] +} + +func ExampleIntersperse() { + s := []int{3, 4, 5} + result := slices.Intersperse(s, 1) + fmt.Println(result) + // Output: [3 1 4 1 5] +} + +func ExampleJoin() { + s := []int{3, 4, 5} + result := slices.Join(s, "; ") + fmt.Println(result) + // Output: 3; 4; 5 +} + +func ExampleLast() { + s := []int{3, 4, 5} + result, _ := slices.Last(s) + fmt.Println(result) + // Output: 5 +} + func ExampleMin() { s := []int{42, 7, 13} min, _ := slices.Min(s) @@ -144,3 +398,271 @@ func ExampleMapAsync() { fmt.Println(pages) // [ ] } + +func ExamplePermutations() { + s := []int{1, 2, 3} + ch := slices.Permutations(s, 2) + result := make([][]int, 0) + for x := range ch { + result = append(result, x) + } + fmt.Println(result) + // Output: [[1 2] [1 3] [2 1] [2 3] [3 1] [3 2]] +} + +func ExampleProduct() { + s := []int{1, 2, 3} + ch := slices.Product(s, 2) + result := make([][]int, 0) + for x := range ch { + result = append(result, x) + } + fmt.Println(result) + // Output: [[1 1] [1 2] [1 3] [2 1] [2 2] [2 3] [3 1] [3 2] [3 3]] +} + +func ExampleProduct2() { + s1 := []int{1, 2} + s2 := []int{3, 4} + ch := slices.Product2(s1, s2) + result := make([][]int, 0) + for x := range ch { + result = append(result, x) + } + fmt.Println(result) + // Output: [[1 3] [1 4] [2 3] [2 4]] +} + +func ExampleReduce() { + s := []int{3, 4, 5} + sum := func(a, b int) int { + fmt.Printf("Received %d and %d\n", a, b) + return a + b + } + result := slices.Reduce(s, 0, sum) + fmt.Printf("Result is %d\n", result) + // Output: + // Received 3 and 0 + // Received 4 and 3 + // Received 5 and 7 + // Result is 12 +} + +func ExampleReduceAsync() { + s := []int{3, 4, 5} + sum := func(a, b int) int { return a + b } + result := slices.ReduceAsync(s, 0, sum) + fmt.Println(result) + // Output: 12 +} + +func ExampleReduceWhile() { + s := []int{3, 4, 5, 6} + sum := func(a, b int) (int, error) { + fmt.Printf("Received %d and %d\n", a, b) + if a == 6 { + return b, errors.New("got six") + } + return a + b, nil + } + result, err := slices.ReduceWhile(s, 0, sum) + fmt.Printf("Result is %d\n", result) + fmt.Printf("Error is '%v'\n", err) + // Output: + // Received 3 and 0 + // Received 4 and 3 + // Received 5 and 7 + // Received 6 and 12 + // Result is 12 + // Error is 'got six' +} + +func ExampleReject() { + s := []int{4, 5, 6, 7, 8, 10, 12, 13} + odd := func(x int) bool { return x%2 == 1 } + result := slices.Reject(s, odd) + fmt.Println(result) + // Output: [4 6 8 10 12] +} + +func ExampleRepeat() { + s := []int{4, 5, 6} + result := slices.Repeat(s, 3) + fmt.Println(result) + // Output: [4 5 6 4 5 6 4 5 6] +} + +func ExampleReverse() { + s := []int{3, 4, 5} + result := slices.Reverse(s) + fmt.Println(result) + // Output: [5 4 3] +} + +func ExampleSame() { + s := []int{3, 3, 3, 3} + result := slices.Same(s) + fmt.Println(result) + // Output: true +} + +func ExampleScan() { + s := []int{3, 4, 5} + sum := func(a, b int) int { return a + b } + result := slices.Scan(s, 0, sum) + fmt.Println(result) + // Output: [3 7 12] +} + +func ExampleShrink() { + s := make([]int, 1, 4) + fmt.Printf("Before: len=%d, cap=%d\n", len(s), cap(s)) + r := slices.Shrink(s) + fmt.Printf("After: len=%d, cap=%d\n", len(r), cap(r)) + // Output: + // Before: len=1, cap=4 + // After: len=1, cap=1 +} + +func ExampleShuffle() { + s := []int{3, 4, 5, 6, 7, 8} + slices.Shuffle(s, 13) + fmt.Println(s) + // Output: [7 8 5 3 6 4] +} + +func ExampleSort() { + s := []int{7, 8, 5, 3, 6, 4} + result := slices.Sort(s) + fmt.Println(result) + // Output: [3 4 5 6 7 8] +} + +func ExampleSorted() { + s := []int{3, 4, 5, 6, 7, 8} + result := slices.Sorted(s) + fmt.Println(result) + // Output: true +} + +func ExampleSplit() { + s := []int{3, 4, 1, 5, 1, 1, 6, 7, 1} + result := slices.Split(s, 1) + fmt.Println(result) + // Output: [[3 4] [5] [] [6 7] []] +} + +func ExampleStartsWith() { + s := []int{3, 4, 5, 6, 7, 8} + result := slices.StartsWith(s, []int{3, 4}) + fmt.Println(result) + // Output: true +} + +func ExampleSum() { + s := []int{3, 4, 5} + result := slices.Sum(s) + fmt.Println(result) + // Output: 12 +} + +func ExampleTakeEvery() { + s := []int{3, 4, 5, 6, 7, 8} + result, _ := slices.TakeEvery(s, 2, 0) + fmt.Println(result) + // Output: [3 5 7] +} + +func ExampleTakeRandom() { + s := []int{3, 4, 5, 6, 7, 8} + result, _ := slices.TakeRandom(s, 3, 13) + fmt.Println(result) + // Output: [7 8 5] +} + +func ExampleTakeWhile() { + s := []int{3, 5, 7, 8, 9, 10, 11} + odd := func(x int) bool { return x%2 == 1 } + result := slices.TakeWhile(s, odd) + fmt.Println(result) + // Output: [3 5 7] +} + +func ExampleToChannel() { + s := []int{3, 4, 5} + ch := slices.ToChannel(s) + result := make([]int, 0) + for x := range ch { + result = append(result, x) + } + fmt.Println(result) + // Output: [3 4 5] +} + +func ExampleToKeys() { + s := []int{3, 4, 5} + result := slices.ToKeys(s, 2) + fmt.Println(result) + // Output: map[3:2 4:2 5:2] +} + +func ExampleToMap() { + s := []int{3, 4, 5} + result := slices.ToMap(s) + fmt.Println(result) + // Output: map[0:3 1:4 2:5] +} + +func ExampleToMapGroupedBy() { + s := []int{3, 4, 5, 13, 23, 14, 25, 34} + lastDigit := func(x int) int { return x % 10 } + result := slices.ToMapGroupedBy(s, lastDigit) + fmt.Println(result) + // Output: map[3:[3 13 23] 4:[4 14 34] 5:[5 25]] +} + +func ExampleUniq() { + s := []int{3, 3, 4, 5, 4, 3, 3} + result := slices.Uniq(s) + fmt.Println(result) + // Output: [3 4 5] +} + +func ExampleUnique() { + s := []int{3, 4, 5, 3} + result := slices.Unique(s) + fmt.Println(result) + // Output: false +} + +func ExampleWindow() { + s := []int{3, 4, 5, 6} + result, _ := slices.Window(s, 2) + fmt.Println(result) + // Output: [[3 4] [4 5] [5 6]] +} + +func ExampleWithout() { + s := []int{3, 4, 5, 6, 3, 4, 5, 6, 7, 8} + result := slices.Without(s, 4, 5) + fmt.Println(result) + // Output: [3 6 3 6 7 8] +} + +func ExampleWrap() { + result := slices.Wrap(4) + fmt.Println(result) + // Output: [4] +} + +func ExampleZip() { + s1 := []int{3, 4, 5} + s2 := []int{6, 7, 8, 9} + ch := slices.Zip(s1, s2) + result := make([][]int, 0) + for x := range ch { + result = append(result, x) + } + fmt.Println(result) + // Output: [[3 6] [4 7] [5 8]] +} diff --git a/slices/slice.go b/slices/slice.go index a35d11e..324d86c 100644 --- a/slices/slice.go +++ b/slices/slice.go @@ -222,9 +222,9 @@ func Equal[S1 ~[]T, S2 ~[]T, T comparable](items S1, other S2) bool { return true } -// Grow increases the slice capacity by n elements. +// Grow increases the slice capacity to fit at least n more elements. // -// So, for cap(slice)=8 and n=2, the result will have cap at least 10. +// So, for len(slice)=8 and n=2, the result will have cap at least 10. // // The function can be used to reduce allocations when inserting more elements // into an existing slice. @@ -342,7 +342,6 @@ func Min[S ~[]T, T constraints.Ordered](items S) (T, error) { } // Permutations returns successive size-length permutations of elements from the slice. -// {1, 2, 3} -> {1, 2}, {1, 3}, {2, 1}, {2, 3}, {3, 1}, {3, 2} func Permutations[T any](items []T, size int) chan []T { c := make(chan []T, 1) go func() { @@ -377,7 +376,6 @@ func permutations[T any](items []T, c chan []T, size int, left []T, right []T) { } // Product returns cortesian product of elements -// {{1, 2}, {3, 4}} -> {1, 3}, {1, 4}, {2, 3}, {2, 4} func Product[S ~[]T, T any](items S, repeat int) chan []T { c := make(chan []T, 1) go func() { @@ -634,8 +632,7 @@ func Unique[S ~[]T, T comparable](items S) bool { return true } -// Window makes sliding window for a given slice: -// ({1,2,3}, 2) -> (1,2), (2,3) +// Window makes sliding window for the given slice func Window[S ~[]T, T any](items S, size int) ([]S, error) { if size <= 0 { return nil, ErrNonPositiveValue diff --git a/slices/slices.go b/slices/slices.go index 4a9a800..be7a376 100644 --- a/slices/slices.go +++ b/slices/slices.go @@ -33,8 +33,7 @@ func Intersect2[S1 ~[]T, S2 ~[]T, T comparable](items1 S1, items2 S2) []T { return result } -// Product returns cortesian product of elements -// {{1, 2}, {3, 4}} -> {1, 3}, {1, 4}, {2, 3}, {2, 4} +// Product returns cortesian product of elements in the given slices. func Product2[T any](items ...[]T) chan []T { c := make(chan []T, 1) go product2(items, c, []T{}, 0) diff --git a/tests/test_docs.py b/tests/test_docs.py new file mode 100644 index 0000000..a9bf220 --- /dev/null +++ b/tests/test_docs.py @@ -0,0 +1,40 @@ +from __future__ import annotations +from pathlib import Path +from typing import Iterator +import pytest + + +def get_funcs(pkg: str) -> Iterator[str]: + for fpath in Path(pkg).iterdir(): + if fpath.suffix != '.go': + continue + if fpath.stem.endswith('_test'): + continue + content = fpath.read_text() + for line in content.splitlines(): + if not line.startswith('func '): + continue + line = line.removeprefix('func ') + fname = line.split('[')[0] + if not fname[0].isupper(): + continue + yield fname + + +def get_examples(pkg: str) -> Iterator[str]: + fpath = Path(pkg, 'examples_test.go') + assert fpath.is_file() + content = fpath.read_text() + for line in content.splitlines(): + if not line.startswith('func Example'): + continue + line = line.removeprefix('func Example') + fname = line.split('(')[0] + yield fname + + +@pytest.mark.parametrize('pkg', ['slices']) +def test_all_have_examples(pkg: str) -> None: + funcs = set(get_funcs(pkg)) + examples = set(get_examples(pkg)) + assert funcs == examples