1
0
mirror of https://github.com/IBM/fp-go.git synced 2025-11-23 22:14:53 +02:00
Files
fp-go/v2/iterator/iter/iter_test.go

590 lines
14 KiB
Go
Raw Normal View History

// Copyright (c) 2023 - 2025 IBM Corp.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package iter
import (
"fmt"
"maps"
"slices"
"strings"
"testing"
F "github.com/IBM/fp-go/v2/function"
N "github.com/IBM/fp-go/v2/number"
O "github.com/IBM/fp-go/v2/option"
S "github.com/IBM/fp-go/v2/string"
"github.com/stretchr/testify/assert"
)
// Helper function to collect sequence into a slice
func toSlice[T any](seq Seq[T]) []T {
return slices.Collect(seq)
}
// Helper function to collect Seq2 into a map
func toMap[K comparable, V any](seq Seq2[K, V]) map[K]V {
return maps.Collect(seq)
}
func TestOf(t *testing.T) {
seq := Of(42)
result := toSlice(seq)
assert.Equal(t, []int{42}, result)
}
func TestOf2(t *testing.T) {
seq := Of2("key", 100)
result := toMap(seq)
assert.Equal(t, map[string]int{"key": 100}, result)
}
func TestFrom(t *testing.T) {
seq := From(1, 2, 3, 4, 5)
result := toSlice(seq)
assert.Equal(t, []int{1, 2, 3, 4, 5}, result)
}
func TestEmpty(t *testing.T) {
seq := Empty[int]()
result := toSlice(seq)
assert.Empty(t, result)
}
func TestMonadMap(t *testing.T) {
seq := From(1, 2, 3)
doubled := MonadMap(seq, N.Mul(2))
result := toSlice(doubled)
assert.Equal(t, []int{2, 4, 6}, result)
}
func TestMap(t *testing.T) {
seq := From(1, 2, 3)
double := Map(N.Mul(2))
result := toSlice(double(seq))
assert.Equal(t, []int{2, 4, 6}, result)
}
func TestMonadMapWithIndex(t *testing.T) {
seq := From("a", "b", "c")
indexed := MonadMapWithIndex(seq, func(i int, s string) string {
return fmt.Sprintf("%d:%s", i, s)
})
result := toSlice(indexed)
assert.Equal(t, []string{"0:a", "1:b", "2:c"}, result)
}
func TestMapWithIndex(t *testing.T) {
seq := From("a", "b", "c")
indexer := MapWithIndex(func(i int, s string) string {
return fmt.Sprintf("%d:%s", i, s)
})
result := toSlice(indexer(seq))
assert.Equal(t, []string{"0:a", "1:b", "2:c"}, result)
}
func TestMonadMapWithKey(t *testing.T) {
seq := Of2("x", 10)
doubled := MonadMapWithKey(seq, func(k string, v int) int { return v * 2 })
result := toMap(doubled)
assert.Equal(t, map[string]int{"x": 20}, result)
}
func TestMapWithKey(t *testing.T) {
seq := Of2("x", 10)
doubler := MapWithKey(func(k string, v int) int { return v * 2 })
result := toMap(doubler(seq))
assert.Equal(t, map[string]int{"x": 20}, result)
}
func TestMonadFilter(t *testing.T) {
seq := From(1, 2, 3, 4, 5)
evens := MonadFilter(seq, func(x int) bool { return x%2 == 0 })
result := toSlice(evens)
assert.Equal(t, []int{2, 4}, result)
}
func TestFilter(t *testing.T) {
seq := From(1, 2, 3, 4, 5)
isEven := Filter(func(x int) bool { return x%2 == 0 })
result := toSlice(isEven(seq))
assert.Equal(t, []int{2, 4}, result)
}
func TestMonadFilterWithIndex(t *testing.T) {
seq := From("a", "b", "c", "d")
oddIndices := MonadFilterWithIndex(seq, func(i int, _ string) bool { return i%2 == 1 })
result := toSlice(oddIndices)
assert.Equal(t, []string{"b", "d"}, result)
}
func TestFilterWithIndex(t *testing.T) {
seq := From("a", "b", "c", "d")
oddIndexFilter := FilterWithIndex(func(i int, _ string) bool { return i%2 == 1 })
result := toSlice(oddIndexFilter(seq))
assert.Equal(t, []string{"b", "d"}, result)
}
func TestMonadFilterWithKey(t *testing.T) {
seq := Of2("x", 10)
filtered := MonadFilterWithKey(seq, func(k string, v int) bool { return v > 5 })
result := toMap(filtered)
assert.Equal(t, map[string]int{"x": 10}, result)
seq2 := Of2("y", 3)
filtered2 := MonadFilterWithKey(seq2, func(k string, v int) bool { return v > 5 })
result2 := toMap(filtered2)
assert.Equal(t, map[string]int{}, result2)
}
func TestFilterWithKey(t *testing.T) {
seq := Of2("x", 10)
filter := FilterWithKey(func(k string, v int) bool { return v > 5 })
result := toMap(filter(seq))
assert.Equal(t, map[string]int{"x": 10}, result)
}
func TestMonadFilterMap(t *testing.T) {
seq := From(1, 2, 3, 4)
result := MonadFilterMap(seq, func(x int) Option[int] {
if x%2 == 0 {
return O.Some(x * 10)
}
return O.None[int]()
})
assert.Equal(t, []int{20, 40}, toSlice(result))
}
func TestFilterMap(t *testing.T) {
seq := From(1, 2, 3, 4)
filterMapper := FilterMap(func(x int) Option[int] {
if x%2 == 0 {
return O.Some(x * 10)
}
return O.None[int]()
})
result := toSlice(filterMapper(seq))
assert.Equal(t, []int{20, 40}, result)
}
func TestMonadFilterMapWithIndex(t *testing.T) {
seq := From("a", "b", "c")
result := MonadFilterMapWithIndex(seq, func(i int, s string) Option[string] {
if i%2 == 0 {
return O.Some(strings.ToUpper(s))
}
return O.None[string]()
})
assert.Equal(t, []string{"A", "C"}, toSlice(result))
}
func TestFilterMapWithIndex(t *testing.T) {
seq := From("a", "b", "c")
filterMapper := FilterMapWithIndex(func(i int, s string) Option[string] {
if i%2 == 0 {
return O.Some(strings.ToUpper(s))
}
return O.None[string]()
})
result := toSlice(filterMapper(seq))
assert.Equal(t, []string{"A", "C"}, result)
}
func TestMonadFilterMapWithKey(t *testing.T) {
seq := Of2("x", 10)
result := MonadFilterMapWithKey(seq, func(k string, v int) Option[int] {
if v > 5 {
return O.Some(v * 2)
}
return O.None[int]()
})
assert.Equal(t, map[string]int{"x": 20}, toMap(result))
}
func TestFilterMapWithKey(t *testing.T) {
seq := Of2("x", 10)
filterMapper := FilterMapWithKey(func(k string, v int) Option[int] {
if v > 5 {
return O.Some(v * 2)
}
return O.None[int]()
})
result := toMap(filterMapper(seq))
assert.Equal(t, map[string]int{"x": 20}, result)
}
func TestMonadChain(t *testing.T) {
seq := From(1, 2)
result := MonadChain(seq, func(x int) Seq[int] {
return From(x, x*10)
})
assert.Equal(t, []int{1, 10, 2, 20}, toSlice(result))
}
func TestChain(t *testing.T) {
seq := From(1, 2)
chainer := Chain(func(x int) Seq[int] {
return From(x, x*10)
})
result := toSlice(chainer(seq))
assert.Equal(t, []int{1, 10, 2, 20}, result)
}
func TestFlatten(t *testing.T) {
seq := From(From(1, 2), From(3, 4))
result := Flatten(seq)
assert.Equal(t, []int{1, 2, 3, 4}, toSlice(result))
}
func TestMonadAp(t *testing.T) {
fns := From(
N.Mul(2),
func(x int) int { return x + 10 },
)
vals := From(1, 2)
result := MonadAp(fns, vals)
assert.Equal(t, []int{2, 4, 11, 12}, toSlice(result))
}
func TestAp(t *testing.T) {
fns := From(
N.Mul(2),
func(x int) int { return x + 10 },
)
vals := From(1, 2)
applier := Ap[int](vals)
result := toSlice(applier(fns))
assert.Equal(t, []int{2, 4, 11, 12}, result)
}
func TestApCurried(t *testing.T) {
f := F.Curry3(func(s1 string, n int, s2 string) string {
return fmt.Sprintf("%s-%d-%s", s1, n, s2)
})
result := F.Pipe4(
Of(f),
Ap[func(int) func(string) string](From("a", "b")),
Ap[func(string) string](From(1, 2)),
Ap[string](From("c", "d")),
toSlice[string],
)
expected := []string{"a-1-c", "a-1-d", "a-2-c", "a-2-d", "b-1-c", "b-1-d", "b-2-c", "b-2-d"}
assert.Equal(t, expected, result)
}
func TestMakeBy(t *testing.T) {
seq := MakeBy(5, func(i int) int { return i * i })
result := toSlice(seq)
assert.Equal(t, []int{0, 1, 4, 9, 16}, result)
}
func TestMakeByZero(t *testing.T) {
seq := MakeBy(0, func(i int) int { return i })
result := toSlice(seq)
assert.Empty(t, result)
}
func TestMakeByNegative(t *testing.T) {
seq := MakeBy(-5, func(i int) int { return i })
result := toSlice(seq)
assert.Empty(t, result)
}
func TestReplicate(t *testing.T) {
seq := Replicate(3, "hello")
result := toSlice(seq)
assert.Equal(t, []string{"hello", "hello", "hello"}, result)
}
func TestMonadReduce(t *testing.T) {
seq := From(1, 2, 3, 4)
sum := MonadReduce(seq, func(acc, x int) int { return acc + x }, 0)
assert.Equal(t, 10, sum)
}
func TestReduce(t *testing.T) {
seq := From(1, 2, 3, 4)
sum := Reduce(func(acc, x int) int { return acc + x }, 0)
result := sum(seq)
assert.Equal(t, 10, result)
}
func TestMonadReduceWithIndex(t *testing.T) {
seq := From(10, 20, 30)
result := MonadReduceWithIndex(seq, func(i, acc, x int) int {
return acc + (i * x)
}, 0)
// 0*10 + 1*20 + 2*30 = 0 + 20 + 60 = 80
assert.Equal(t, 80, result)
}
func TestReduceWithIndex(t *testing.T) {
seq := From(10, 20, 30)
reducer := ReduceWithIndex(func(i, acc, x int) int {
return acc + (i * x)
}, 0)
result := reducer(seq)
assert.Equal(t, 80, result)
}
func TestMonadReduceWithKey(t *testing.T) {
seq := Of2("x", 10)
result := MonadReduceWithKey(seq, func(k string, acc, v int) int {
return acc + v
}, 0)
assert.Equal(t, 10, result)
}
func TestReduceWithKey(t *testing.T) {
seq := Of2("x", 10)
reducer := ReduceWithKey(func(k string, acc, v int) int {
return acc + v
}, 0)
result := reducer(seq)
assert.Equal(t, 10, result)
}
func TestMonadFold(t *testing.T) {
seq := From("Hello", " ", "World")
result := MonadFold(seq, S.Monoid)
assert.Equal(t, "Hello World", result)
}
func TestFold(t *testing.T) {
seq := From("Hello", " ", "World")
folder := Fold(S.Monoid)
result := folder(seq)
assert.Equal(t, "Hello World", result)
}
func TestMonadFoldMap(t *testing.T) {
seq := From(1, 2, 3)
result := MonadFoldMap(seq, func(x int) string {
return fmt.Sprintf("%d", x)
}, S.Monoid)
assert.Equal(t, "123", result)
}
func TestFoldMap(t *testing.T) {
seq := From(1, 2, 3)
folder := FoldMap[int](S.Monoid)(func(x int) string {
return fmt.Sprintf("%d", x)
})
result := folder(seq)
assert.Equal(t, "123", result)
}
func TestMonadFoldMapWithIndex(t *testing.T) {
seq := From("a", "b", "c")
result := MonadFoldMapWithIndex(seq, func(i int, s string) string {
return fmt.Sprintf("%d:%s ", i, s)
}, S.Monoid)
assert.Equal(t, "0:a 1:b 2:c ", result)
}
func TestFoldMapWithIndex(t *testing.T) {
seq := From("a", "b", "c")
folder := FoldMapWithIndex[string](S.Monoid)(func(i int, s string) string {
return fmt.Sprintf("%d:%s ", i, s)
})
result := folder(seq)
assert.Equal(t, "0:a 1:b 2:c ", result)
}
func TestMonadFoldMapWithKey(t *testing.T) {
seq := Of2("x", 10)
result := MonadFoldMapWithKey(seq, func(k string, v int) string {
return fmt.Sprintf("%s:%d ", k, v)
}, S.Monoid)
assert.Equal(t, "x:10 ", result)
}
func TestFoldMapWithKey(t *testing.T) {
seq := Of2("x", 10)
folder := FoldMapWithKey[string, int](S.Monoid)(func(k string, v int) string {
return fmt.Sprintf("%s:%d ", k, v)
})
result := folder(seq)
assert.Equal(t, "x:10 ", result)
}
func TestMonadFlap(t *testing.T) {
fns := From(
N.Mul(2),
func(x int) int { return x + 10 },
)
result := MonadFlap(fns, 5)
assert.Equal(t, []int{10, 15}, toSlice(result))
}
func TestFlap(t *testing.T) {
fns := From(
N.Mul(2),
func(x int) int { return x + 10 },
)
flapper := Flap[int](5)
result := toSlice(flapper(fns))
assert.Equal(t, []int{10, 15}, result)
}
func TestPrepend(t *testing.T) {
seq := From(2, 3, 4)
result := Prepend(1)(seq)
assert.Equal(t, []int{1, 2, 3, 4}, toSlice(result))
}
func TestAppend(t *testing.T) {
seq := From(1, 2, 3)
result := Append(4)(seq)
assert.Equal(t, []int{1, 2, 3, 4}, toSlice(result))
}
func TestMonadZip(t *testing.T) {
seqA := From(1, 2, 3)
seqB := From("a", "b")
result := MonadZip(seqB, seqA)
var pairs []string
for a, b := range result {
pairs = append(pairs, fmt.Sprintf("%d:%s", a, b))
}
assert.Equal(t, []string{"1:a", "2:b"}, pairs)
}
func TestZip(t *testing.T) {
seqA := From(1, 2, 3)
seqB := From("a", "b", "c")
zipWithA := Zip[int, string](seqA)
result := zipWithA(seqB)
var pairs []string
for a, b := range result {
pairs = append(pairs, fmt.Sprintf("%d:%s", a, b))
}
assert.Equal(t, []string{"1:a", "2:b", "3:c"}, pairs)
}
func TestMonoid(t *testing.T) {
m := Monoid[int]()
seq1 := From(1, 2)
seq2 := From(3, 4)
result := m.Concat(seq1, seq2)
assert.Equal(t, []int{1, 2, 3, 4}, toSlice(result))
}
func TestMonoidEmpty(t *testing.T) {
m := Monoid[int]()
empty := m.Empty()
assert.Empty(t, toSlice(empty))
}
func TestMonoidAssociativity(t *testing.T) {
m := Monoid[int]()
seq1 := From(1, 2)
seq2 := From(3, 4)
seq3 := From(5, 6)
// (seq1 + seq2) + seq3
left := m.Concat(m.Concat(seq1, seq2), seq3)
// seq1 + (seq2 + seq3)
right := m.Concat(seq1, m.Concat(seq2, seq3))
assert.Equal(t, toSlice(left), toSlice(right))
}
func TestMonoidIdentity(t *testing.T) {
m := Monoid[int]()
seq := From(1, 2, 3)
empty := m.Empty()
// seq + empty = seq
leftIdentity := m.Concat(seq, empty)
assert.Equal(t, []int{1, 2, 3}, toSlice(leftIdentity))
// empty + seq = seq
rightIdentity := m.Concat(empty, seq)
assert.Equal(t, []int{1, 2, 3}, toSlice(rightIdentity))
}
func TestPipelineComposition(t *testing.T) {
// Test a complex pipeline
result := F.Pipe4(
From(1, 2, 3, 4, 5, 6),
Filter(func(x int) bool { return x%2 == 0 }),
Map(func(x int) int { return x * 10 }),
Prepend(0),
toSlice[int],
)
assert.Equal(t, []int{0, 20, 40, 60}, result)
}
func TestLazyEvaluation(t *testing.T) {
// Test that operations are lazy
callCount := 0
seq := From(1, 2, 3, 4, 5)
mapped := MonadMap(seq, func(x int) int {
callCount++
return x * 2
})
// No calls yet since we haven't iterated
assert.Equal(t, 0, callCount)
// Iterate only first 2 elements
count := 0
for range mapped {
count++
if count == 2 {
break
}
}
// Should have called the function only twice
assert.Equal(t, 2, callCount)
}
func ExampleFoldMap() {
seq := From("a", "b", "c")
fold := FoldMap[string](S.Monoid)(strings.ToUpper)
result := fold(seq)
fmt.Println(result)
// Output: ABC
}
func ExampleChain() {
seq := From(1, 2)
result := F.Pipe2(
seq,
Chain(func(x int) Seq[int] {
return From(x, x*10)
}),
toSlice[int],
)
fmt.Println(result)
// Output: [1 10 2 20]
}
func ExampleMonoid() {
m := Monoid[int]()
seq1 := From(1, 2, 3)
seq2 := From(4, 5, 6)
combined := m.Concat(seq1, seq2)
result := toSlice(combined)
fmt.Println(result)
// Output: [1 2 3 4 5 6]
}