1
0
mirror of https://github.com/duke-git/lancet.git synced 2026-02-04 12:52:28 +08:00

test: add example for stream package

This commit is contained in:
dudaodong
2023-04-05 18:52:27 +08:00
parent 6e7300bbbf
commit 5b9b4c4344
3 changed files with 422 additions and 10 deletions

View File

@@ -1,9 +1,8 @@
// Copyright 2023 dudaodong@gmail.com. All rights resulterved. // Copyright 2023 dudaodong@gmail.com. All rights resulterved.
// Use of this source code is governed by MIT license // Use of this source code is governed by MIT license
// Package stream implements a sequence of elements supporting sequential and parallel aggregate operations. // Package stream implements a sequence of elements supporting sequential and operations.
// this package is an experiment to explore if stream in go can work as the way java does. it's complete, but not // this package is an experiment to explore if stream in go can work as the way java does. it's function is very limited.
// powerful like other libs
package stream package stream
import ( import (
@@ -53,12 +52,13 @@ type stream[T any] struct {
} }
// Of creates a stream stream whose elements are the specified values. // Of creates a stream stream whose elements are the specified values.
// Play: https://go.dev/play/p/jI6_iZZuVFE
func Of[T any](elems ...T) stream[T] { func Of[T any](elems ...T) stream[T] {
return FromSlice(elems) return FromSlice(elems)
} }
// Generate stream where each element is generated by the provided generater function // Generate stream where each element is generated by the provided generater function
// generater function: func() func() (item T, ok bool) {} // Play: https://go.dev/play/p/rkOWL1yA3j9
func Generate[T any](generator func() func() (item T, ok bool)) stream[T] { func Generate[T any](generator func() func() (item T, ok bool)) stream[T] {
source := make([]T, 0) source := make([]T, 0)
@@ -75,11 +75,13 @@ func Generate[T any](generator func() func() (item T, ok bool)) stream[T] {
} }
// FromSlice creates stream from slice. // FromSlice creates stream from slice.
// Play: https://go.dev/play/p/wywTO0XZtI4
func FromSlice[T any](source []T) stream[T] { func FromSlice[T any](source []T) stream[T] {
return stream[T]{source: source} return stream[T]{source: source}
} }
// FromChannel creates stream from channel. // FromChannel creates stream from channel.
// Play: https://go.dev/play/p/9TZYugGMhXZ
func FromChannel[T any](source <-chan T) stream[T] { func FromChannel[T any](source <-chan T) stream[T] {
s := make([]T, 0) s := make([]T, 0)
@@ -91,6 +93,7 @@ func FromChannel[T any](source <-chan T) stream[T] {
} }
// FromRange creates a number stream from start to end. both start and end are included. [start, end] // FromRange creates a number stream from start to end. both start and end are included. [start, end]
// Play: https://go.dev/play/p/9Ex1-zcg-B-
func FromRange[T constraints.Integer | constraints.Float](start, end, step T) stream[T] { func FromRange[T constraints.Integer | constraints.Float](start, end, step T) stream[T] {
if end < start { if end < start {
panic("stream.FromRange: param start should be before param end") panic("stream.FromRange: param start should be before param end")
@@ -109,6 +112,7 @@ func FromRange[T constraints.Integer | constraints.Float](start, end, step T) st
} }
// Concat creates a lazily concatenated stream whose elements are all the elements of the first stream followed by all the elements of the second stream. // Concat creates a lazily concatenated stream whose elements are all the elements of the first stream followed by all the elements of the second stream.
// Play: https://go.dev/play/p/HM4OlYk_OUC
func Concat[T any](a, b stream[T]) stream[T] { func Concat[T any](a, b stream[T]) stream[T] {
source := make([]T, 0) source := make([]T, 0)
@@ -119,6 +123,7 @@ func Concat[T any](a, b stream[T]) stream[T] {
} }
// Distinct returns a stream that removes the duplicated items. // Distinct returns a stream that removes the duplicated items.
// Play: https://go.dev/play/p/eGkOSrm64cB
func (s stream[T]) Distinct() stream[T] { func (s stream[T]) Distinct() stream[T] {
source := make([]T, 0) source := make([]T, 0)
@@ -147,6 +152,7 @@ func hashKey(data any) string {
} }
// Filter returns a stream consisting of the elements of this stream that match the given predicate. // Filter returns a stream consisting of the elements of this stream that match the given predicate.
// Play: https://go.dev/play/p/MFlSANo-buc
func (s stream[T]) Filter(predicate func(item T) bool) stream[T] { func (s stream[T]) Filter(predicate func(item T) bool) stream[T] {
source := make([]T, 0) source := make([]T, 0)
@@ -160,6 +166,7 @@ func (s stream[T]) Filter(predicate func(item T) bool) stream[T] {
} }
// Map returns a stream consisting of the elements of this stream that apply the given function to elements of stream. // Map returns a stream consisting of the elements of this stream that apply the given function to elements of stream.
// Play: https://go.dev/play/p/OtNQUImdYko
func (s stream[T]) Map(mapper func(item T) T) stream[T] { func (s stream[T]) Map(mapper func(item T) T) stream[T] {
source := make([]T, s.Count()) source := make([]T, s.Count())
@@ -171,6 +178,7 @@ func (s stream[T]) Map(mapper func(item T) T) stream[T] {
} }
// Peek returns a stream consisting of the elements of this stream, additionally performing the provided action on each element as elements are consumed from the resulting stream. // Peek returns a stream consisting of the elements of this stream, additionally performing the provided action on each element as elements are consumed from the resulting stream.
// Play: https://go.dev/play/p/u1VNzHs6cb2
func (s stream[T]) Peek(consumer func(item T)) stream[T] { func (s stream[T]) Peek(consumer func(item T)) stream[T] {
for _, v := range s.source { for _, v := range s.source {
consumer(v) consumer(v)
@@ -181,6 +189,7 @@ func (s stream[T]) Peek(consumer func(item T)) stream[T] {
// Skip returns a stream consisting of the remaining elements of this stream after discarding the first n elements of the stream. // Skip returns a stream consisting of the remaining elements of this stream after discarding the first n elements of the stream.
// If this stream contains fewer than n elements then an empty stream will be returned. // If this stream contains fewer than n elements then an empty stream will be returned.
// Play: https://go.dev/play/p/fNdHbqjahum
func (s stream[T]) Skip(n int) stream[T] { func (s stream[T]) Skip(n int) stream[T] {
if n <= 0 { if n <= 0 {
return s return s
@@ -201,6 +210,7 @@ func (s stream[T]) Skip(n int) stream[T] {
} }
// Limit returns a stream consisting of the elements of this stream, truncated to be no longer than maxSize in length. // Limit returns a stream consisting of the elements of this stream, truncated to be no longer than maxSize in length.
// Play: https://go.dev/play/p/qsO4aniDcGf
func (s stream[T]) Limit(maxSize int) stream[T] { func (s stream[T]) Limit(maxSize int) stream[T] {
if s.source == nil { if s.source == nil {
return s return s
@@ -220,6 +230,7 @@ func (s stream[T]) Limit(maxSize int) stream[T] {
} }
// AllMatch returns whether all elements of this stream match the provided predicate. // AllMatch returns whether all elements of this stream match the provided predicate.
// Play: https://go.dev/play/p/V5TBpVRs-Cx
func (s stream[T]) AllMatch(predicate func(item T) bool) bool { func (s stream[T]) AllMatch(predicate func(item T) bool) bool {
for _, v := range s.source { for _, v := range s.source {
if !predicate(v) { if !predicate(v) {
@@ -231,6 +242,7 @@ func (s stream[T]) AllMatch(predicate func(item T) bool) bool {
} }
// AnyMatch returns whether any elements of this stream match the provided predicate. // AnyMatch returns whether any elements of this stream match the provided predicate.
// Play: https://go.dev/play/p/PTCnWn4OxSn
func (s stream[T]) AnyMatch(predicate func(item T) bool) bool { func (s stream[T]) AnyMatch(predicate func(item T) bool) bool {
for _, v := range s.source { for _, v := range s.source {
if predicate(v) { if predicate(v) {
@@ -242,11 +254,13 @@ func (s stream[T]) AnyMatch(predicate func(item T) bool) bool {
} }
// NoneMatch returns whether no elements of this stream match the provided predicate. // NoneMatch returns whether no elements of this stream match the provided predicate.
// Play: https://go.dev/play/p/iWS64pL1oo3
func (s stream[T]) NoneMatch(predicate func(item T) bool) bool { func (s stream[T]) NoneMatch(predicate func(item T) bool) bool {
return !s.AnyMatch(predicate) return !s.AnyMatch(predicate)
} }
// ForEach performs an action for each element of this stream. // ForEach performs an action for each element of this stream.
// Play: https://go.dev/play/p/Dsm0fPqcidk
func (s stream[T]) ForEach(action func(item T)) { func (s stream[T]) ForEach(action func(item T)) {
for _, v := range s.source { for _, v := range s.source {
action(v) action(v)
@@ -254,20 +268,23 @@ func (s stream[T]) ForEach(action func(item T)) {
} }
// Reduce performs a reduction on the elements of this stream, using an associative accumulation function, and returns an Optional describing the reduced value, if any. // Reduce performs a reduction on the elements of this stream, using an associative accumulation function, and returns an Optional describing the reduced value, if any.
func (s stream[T]) Reduce(init T, accumulator func(a, b T) T) T { // Play: https://go.dev/play/p/g-xkHiuIpTi
func (s stream[T]) Reduce(initial T, accumulator func(a, b T) T) T {
for _, v := range s.source { for _, v := range s.source {
init = accumulator(init, v) initial = accumulator(initial, v)
} }
return init return initial
} }
// Count returns the count of elements in the stream. // Count returns the count of elements in the stream.
// Play: https://go.dev/play/p/r3koY6y_Xo-
func (s stream[T]) Count() int { func (s stream[T]) Count() int {
return len(s.source) return len(s.source)
} }
// FindFirst returns the first element of this stream and true, or zero value and false if the stream is empty. // FindFirst returns the first element of this stream and true, or zero value and false if the stream is empty.
// Play: https://go.dev/play/p/9xEf0-6C1e3
func (s stream[T]) FindFirst() (T, bool) { func (s stream[T]) FindFirst() (T, bool) {
var result T var result T
@@ -279,6 +296,7 @@ func (s stream[T]) FindFirst() (T, bool) {
} }
// Reverse returns a stream whose elements are reverse order of given stream. // Reverse returns a stream whose elements are reverse order of given stream.
// Play: https://go.dev/play/p/A8_zkJnLHm4
func (s stream[T]) Reverse() stream[T] { func (s stream[T]) Reverse() stream[T] {
l := len(s.source) l := len(s.source)
source := make([]T, l) source := make([]T, l)
@@ -290,6 +308,7 @@ func (s stream[T]) Reverse() stream[T] {
} }
// Range returns a stream whose elements are in the range from start(included) to end(excluded) original stream. // Range returns a stream whose elements are in the range from start(included) to end(excluded) original stream.
// Play: https://go.dev/play/p/indZY5V2f4j
func (s stream[T]) Range(start, end int) stream[T] { func (s stream[T]) Range(start, end int) stream[T] {
if start < 0 { if start < 0 {
start = 0 start = 0
@@ -315,6 +334,7 @@ func (s stream[T]) Range(start, end int) stream[T] {
} }
// Sorted returns a stream consisting of the elements of this stream, sorted according to the provided less function. // Sorted returns a stream consisting of the elements of this stream, sorted according to the provided less function.
// Play: https://go.dev/play/p/XXtng5uonFj
func (s stream[T]) Sorted(less func(a, b T) bool) stream[T] { func (s stream[T]) Sorted(less func(a, b T) bool) stream[T] {
source := []T{} source := []T{}
source = append(source, s.source...) source = append(source, s.source...)
@@ -326,6 +346,7 @@ func (s stream[T]) Sorted(less func(a, b T) bool) stream[T] {
// Max returns the maximum element of this stream according to the provided less function. // Max returns the maximum element of this stream according to the provided less function.
// less: a > b // less: a > b
// Play: https://go.dev/play/p/fm-1KOPtGzn
func (s stream[T]) Max(less func(a, b T) bool) (T, bool) { func (s stream[T]) Max(less func(a, b T) bool) (T, bool) {
var max T var max T
@@ -343,6 +364,7 @@ func (s stream[T]) Max(less func(a, b T) bool) (T, bool) {
// Min returns the minimum element of this stream according to the provided less function. // Min returns the minimum element of this stream according to the provided less function.
// less: a < b // less: a < b
// Play: https://go.dev/play/p/vZfIDgGNRe_0
func (s stream[T]) Min(less func(a, b T) bool) (T, bool) { func (s stream[T]) Min(less func(a, b T) bool) (T, bool) {
var min T var min T
@@ -360,6 +382,7 @@ func (s stream[T]) Min(less func(a, b T) bool) (T, bool) {
} }
// ToSlice return the elements in the stream. // ToSlice return the elements in the stream.
// Play: https://go.dev/play/p/jI6_iZZuVFE
func (s stream[T]) ToSlice() []T { func (s stream[T]) ToSlice() []T {
return s.source return s.source
} }

View File

@@ -0,0 +1,375 @@
package stream
import (
"fmt"
)
func ExampleOf() {
s := Of(1, 2, 3)
data := s.ToSlice()
fmt.Println(data)
// Output:
// [1 2 3]
}
func ExampleFromSlice() {
s := FromSlice([]int{1, 2, 3})
data := s.ToSlice()
fmt.Println(data)
// Output:
// [1 2 3]
}
func ExampleFromChannel() {
ch := make(chan int)
go func() {
for i := 1; i < 4; i++ {
ch <- i
}
close(ch)
}()
s := FromChannel(ch)
data := s.ToSlice()
fmt.Println(data)
// Output:
// [1 2 3]
}
func ExampleFromRange() {
s := FromRange(1, 5, 1)
data := s.ToSlice()
fmt.Println(data)
// Output:
// [1 2 3 4 5]
}
func ExampleGenerate() {
n := 0
max := 4
generator := func() func() (int, bool) {
return func() (int, bool) {
n++
return n, n < max
}
}
s := Generate(generator)
data := s.ToSlice()
fmt.Println(data)
// Output:
// [1 2 3]
}
func ExampleConcat() {
s1 := FromSlice([]int{1, 2, 3})
s2 := FromSlice([]int{4, 5, 6})
s := Concat(s1, s2)
data := s.ToSlice()
fmt.Println(data)
// Output:
// [1 2 3 4 5 6]
}
func ExampleStream_Distinct() {
originalStream := FromSlice([]int{1, 2, 2, 3, 3, 3})
distinctStream := originalStream.Distinct()
data1 := originalStream.ToSlice()
data2 := distinctStream.ToSlice()
fmt.Println(data1)
fmt.Println(data2)
// Output:
// [1 2 2 3 3 3]
// [1 2 3]
}
func ExampleStream_Filter() {
original := FromSlice([]int{1, 2, 3, 4, 5})
isEven := func(n int) bool {
return n%2 == 0
}
even := original.Filter(isEven)
fmt.Println(even.ToSlice())
// Output:
// [2 4]
}
func ExampleStream_Map() {
original := FromSlice([]int{1, 2, 3})
addOne := func(n int) int {
return n + 1
}
increament := original.Map(addOne)
fmt.Println(increament.ToSlice())
// Output:
// [2 3 4]
}
func ExampleStream_Peek() {
original := FromSlice([]int{1, 2, 3})
data := []string{}
peekStream := original.Peek(func(n int) {
data = append(data, fmt.Sprint("value", n))
})
fmt.Println(original.ToSlice())
fmt.Println(peekStream.ToSlice())
fmt.Println(data)
// Output:
// [1 2 3]
// [1 2 3]
// [value1 value2 value3]
}
func ExampleStream_Skip() {
original := FromSlice([]int{1, 2, 3, 4})
s1 := original.Skip(-1)
s2 := original.Skip(0)
s3 := original.Skip(1)
s4 := original.Skip(5)
fmt.Println(s1.ToSlice())
fmt.Println(s2.ToSlice())
fmt.Println(s3.ToSlice())
fmt.Println(s4.ToSlice())
// Output:
// [1 2 3 4]
// [1 2 3 4]
// [2 3 4]
// []
}
func ExampleStream_Limit() {
original := FromSlice([]int{1, 2, 3, 4})
s1 := original.Limit(-1)
s2 := original.Limit(0)
s3 := original.Limit(1)
s4 := original.Limit(5)
fmt.Println(s1.ToSlice())
fmt.Println(s2.ToSlice())
fmt.Println(s3.ToSlice())
fmt.Println(s4.ToSlice())
// Output:
// []
// []
// [1]
// [1 2 3 4]
}
func ExampleStream_AllMatch() {
original := FromSlice([]int{1, 2, 3})
result1 := original.AllMatch(func(item int) bool {
return item > 0
})
result2 := original.AllMatch(func(item int) bool {
return item > 1
})
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
func ExampleStream_AnyMatch() {
original := FromSlice([]int{1, 2, 3})
result1 := original.AnyMatch(func(item int) bool {
return item > 1
})
result2 := original.AnyMatch(func(item int) bool {
return item > 3
})
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
func ExampleStream_NoneMatch() {
original := FromSlice([]int{1, 2, 3})
result1 := original.NoneMatch(func(item int) bool {
return item > 3
})
result2 := original.NoneMatch(func(item int) bool {
return item > 1
})
fmt.Println(result1)
fmt.Println(result2)
// Output:
// true
// false
}
func ExampleStream_ForEach() {
original := FromSlice([]int{1, 2, 3})
result := 0
original.ForEach(func(item int) {
result += item
})
fmt.Println(result)
// Output:
// 6
}
func ExampleStream_Reduce() {
original := FromSlice([]int{1, 2, 3})
result := 0
original.ForEach(func(item int) {
result += item
})
fmt.Println(result)
// Output:
// 6
}
func ExampleStream_FindFirst() {
original := FromSlice([]int{1, 2, 3})
result, ok := original.FindFirst()
fmt.Println(result)
fmt.Println(ok)
// Output:
// 1
// true
}
func ExampleStream_Reverse() {
original := FromSlice([]int{1, 2, 3})
reverse := original.Reverse()
fmt.Println(reverse.ToSlice())
// Output:
// [3 2 1]
}
func ExampleStream_Range() {
original := FromSlice([]int{1, 2, 3})
s1 := original.Range(0, 0)
s2 := original.Range(0, 1)
s3 := original.Range(0, 3)
s4 := original.Range(1, 2)
fmt.Println(s1.ToSlice())
fmt.Println(s2.ToSlice())
fmt.Println(s3.ToSlice())
fmt.Println(s4.ToSlice())
// Output:
// []
// [1]
// [1 2 3]
// [2]
}
func ExampleStream_Sorted() {
original := FromSlice([]int{4, 2, 1, 3})
sorted := original.Sorted(func(a, b int) bool { return a < b })
fmt.Println(original.ToSlice())
fmt.Println(sorted.ToSlice())
// Output:
// [4 2 1 3]
// [1 2 3 4]
}
func ExampleStream_Max() {
original := FromSlice([]int{4, 2, 1, 3})
max, ok := original.Max(func(a, b int) bool { return a > b })
fmt.Println(max)
fmt.Println(ok)
// Output:
// 4
// true
}
func ExampleStream_Min() {
original := FromSlice([]int{4, 2, 1, 3})
min, ok := original.Min(func(a, b int) bool { return a < b })
fmt.Println(min)
fmt.Println(ok)
// Output:
// 1
// true
}
func ExampleStream_Count() {
s1 := FromSlice([]int{1, 2, 3})
s2 := FromSlice([]int{})
fmt.Println(s1.Count())
fmt.Println(s2.Count())
// Output:
// 3
// 0
}

View File

@@ -147,13 +147,17 @@ func TestStream_Peek(t *testing.T) {
func TestStream_Skip(t *testing.T) { func TestStream_Skip(t *testing.T) {
assert := internal.NewAssert(t, "TestStream_Peek") assert := internal.NewAssert(t, "TestStream_Peek")
stream := FromSlice([]int{1, 2, 3, 4, 5, 6}) stream := FromSlice([]int{1, 2, 3, 4})
s1 := stream.Skip(-1) s1 := stream.Skip(-1)
s2 := stream.Skip(0) s2 := stream.Skip(0)
s3 := stream.Skip(1)
s4 := stream.Skip(2)
assert.Equal([]int{1, 2, 3, 4, 5, 6}, s1.ToSlice()) assert.Equal([]int{1, 2, 3, 4}, s1.ToSlice())
assert.Equal([]int{1, 2, 3, 4, 5, 6}, s2.ToSlice()) assert.Equal([]int{1, 2, 3, 4}, s2.ToSlice())
assert.Equal([]int{2, 3, 4}, s3.ToSlice())
assert.Equal([]int{3, 4}, s4.ToSlice())
} }
func TestStream_Limit(t *testing.T) { func TestStream_Limit(t *testing.T) {
@@ -248,6 +252,16 @@ func TestStream_Reduce(t *testing.T) {
assert.Equal(6, result) assert.Equal(6, result)
} }
func TestStream_Count(t *testing.T) {
assert := internal.NewAssert(t, "TestStream_Count")
s1 := FromSlice([]int{1, 2, 3})
s2 := FromSlice([]int{})
assert.Equal(3, s1.Count())
assert.Equal(0, s2.Count())
}
func TestStream_FindFirst(t *testing.T) { func TestStream_FindFirst(t *testing.T) {
assert := internal.NewAssert(t, "TestStream_FindFirst") assert := internal.NewAssert(t, "TestStream_FindFirst")