Skip to content

Commit

Permalink
Generalized Range stream operation
Browse files Browse the repository at this point in the history
  • Loading branch information
Joseph Fourny committed Dec 12, 2023
1 parent eb2e456 commit a08be73
Show file tree
Hide file tree
Showing 2 changed files with 11 additions and 90 deletions.
44 changes: 5 additions & 39 deletions pkg/stream/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,50 +251,16 @@ func FromIterator[E any](it Iterator[E]) Stream[E] {
}
}

// RangeInteger returns a stream of integers in the range [start, end).
// Range returns a stream that produces elements over a range beginning at `start`, advanced by the `next` function, and ending when `cond` predicate returns false.
//
// Example usage:
//
// s := stream.RangeInteger(1, 4)
// out := stream.DebugString(s) // "<1, 2, 3>"
func RangeInteger[E constraint.Integer](start, end E) Stream[E] {
return func(yield Consumer[E]) bool {
for i := start; i < end; i++ {
if !yield(i) {
return false
}
}
return true
}
}

// RangeIntegerStep returns a stream of integers in the range [start, end) with the given step size.
//
// Example usage:
//
// s := stream.RangeIntegerStep(1, 7, 2)
// s := stream.Range(1, pred.LessThanOrEqual(5), mapper.Increment(2))
// out := stream.DebugString(s) // "<1, 3, 5>"
func RangeIntegerStep[E constraint.Integer](start, end, step E) Stream[E] {
func Range[E any](start E, cond Predicate[E], next Mapper[E, E]) Stream[E] {
return func(yield Consumer[E]) bool {
for i := start; i < end; i += step {
if !yield(i) {
return false
}
}
return true
}
}

// RangeFloatStep returns a stream of floats in the range [start, end) with the given step size.
//
// Example usage:
//
// s := stream.RangeFloatStep(1.0, 3.0, 0.5)
// out := stream.DebugString(s) // "<1.0, 1.5, 2.0, 2.5>"
func RangeFloatStep[E constraint.Float](start, end, step E) Stream[E] {
return func(yield Consumer[E]) bool {
for f := start; f < end; f += step {
if !yield(f) {
for e := start; cond(e); e = next(e) {
if !yield(e) {
return false
}
}
Expand Down
57 changes: 6 additions & 51 deletions pkg/stream/stream_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/jpfourny/papaya/pkg/mapper"
"github.com/jpfourny/papaya/pkg/optional"
"github.com/jpfourny/papaya/pkg/pair"
"github.com/jpfourny/papaya/pkg/pred"
)

func TestEmpty(t *testing.T) {
Expand Down Expand Up @@ -329,73 +330,27 @@ func TestFromChannelCtx(t *testing.T) {
})
}

func TestRangeInteger(t *testing.T) {
func TestRange(t *testing.T) {
t.Run("empty", func(t *testing.T) {
s := RangeInteger(0, 0)
s := Range(0, pred.LessThan(0), mapper.Increment(1))
got := CollectSlice(s)
var want []int
assertElementsMatch(t, got, want)
})

t.Run("non-empty", func(t *testing.T) {
s := RangeInteger(1, 4)
s := Range(1, pred.LessThanOrEqual(5), mapper.Increment(2))
got := CollectSlice(s)
want := []int{1, 2, 3}
want := []int{1, 3, 5}
assertElementsMatch(t, got, want)
})

t.Run("limited", func(t *testing.T) {
s := RangeInteger(1, 4)
s := Range(1, pred.LessThanOrEqual(5), mapper.Increment(2))
got := CollectSlice(Limit(s, 2)) // Stops stream after 2 elements.
want := []int{1, 2}
assertElementsMatch(t, got, want)
})
}

func TestRangeIntegerStep(t *testing.T) {
t.Run("empty", func(t *testing.T) {
s := RangeIntegerStep(0, 0, 1)
got := CollectSlice(s)
var want []int
assertElementsMatch(t, got, want)
})

t.Run("non-empty", func(t *testing.T) {
s := RangeIntegerStep(1, 4, 2)
got := CollectSlice(s)
want := []int{1, 3}
assertElementsMatch(t, got, want)
})

t.Run("limited", func(t *testing.T) {
s := RangeIntegerStep(1, 4, 2)
got := CollectSlice(Limit(s, 1)) // Stops stream after 1 element.
want := []int{1}
assertElementsMatch(t, got, want)
})
}

func TestRangeFloatStep(t *testing.T) {
t.Run("empty", func(t *testing.T) {
s := RangeFloatStep(0.0, 0.0, 1.0)
got := CollectSlice(s)
var want []float64
assertElementsMatch(t, got, want)
})

t.Run("non-empty", func(t *testing.T) {
s := RangeFloatStep(1.0, 4.0, 2.0)
got := CollectSlice(s)
want := []float64{1.0, 3.0}
assertElementsMatch(t, got, want)
})

t.Run("limited", func(t *testing.T) {
s := RangeFloatStep(1.0, 4.0, 2.0)
got := CollectSlice(Limit(s, 1)) // Stops stream after 1 element.
want := []float64{1.0}
assertElementsMatch(t, got, want)
})
}

func TestUnion(t *testing.T) {
Expand Down

0 comments on commit a08be73

Please sign in to comment.