Skip to content

Commit c8b01aa

Browse files
rjNemoclaude
andauthored
feat: add FoldRight function (#48)
- Add FoldRight: fold/reduce from right to left - Useful for non-associative operations - Comprehensive tests including comparison with Reduce - Benchmark included Example: FoldRight([1,2,3], 0, subtract) → 1-(2-(3-0)) = 2 Resolves Issue 20 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com>
1 parent 85f73f6 commit c8b01aa

File tree

2 files changed

+93
-0
lines changed

2 files changed

+93
-0
lines changed

foldright.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package underscore
2+
3+
// FoldRight is like Reduce but processes elements from right to left.
4+
// Also known as foldr in Haskell.
5+
//
6+
// Example: FoldRight([]int{1,2,3}, 0, func(n, acc int) int { return n - acc })
7+
// → 1 - (2 - (3 - 0)) = 1 - (2 - 3) = 1 - (-1) = 2
8+
func FoldRight[T, P any](values []T, acc P, fn func(T, P) P) P {
9+
for i := len(values) - 1; i >= 0; i-- {
10+
acc = fn(values[i], acc)
11+
}
12+
return acc
13+
}

foldright_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package underscore_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
8+
u "github.com/rjNemo/underscore"
9+
)
10+
11+
func TestFoldRight(t *testing.T) {
12+
nums := []int{1, 2, 3, 4}
13+
result := u.FoldRight(nums, 0, func(n, acc int) int { return n + acc })
14+
assert.Equal(t, 10, result)
15+
}
16+
17+
func TestFoldRightEmpty(t *testing.T) {
18+
result := u.FoldRight([]int{}, 42, func(n, acc int) int { return n + acc })
19+
assert.Equal(t, 42, result)
20+
}
21+
22+
func TestFoldRightSingleElement(t *testing.T) {
23+
result := u.FoldRight([]int{5}, 0, func(n, acc int) int { return n + acc })
24+
assert.Equal(t, 5, result)
25+
}
26+
27+
func TestFoldRightSubtraction(t *testing.T) {
28+
// FoldRight: 1 - (2 - (3 - 0)) = 1 - (2 - 3) = 1 - (-1) = 2
29+
nums := []int{1, 2, 3}
30+
result := u.FoldRight(nums, 0, func(n, acc int) int { return n - acc })
31+
assert.Equal(t, 2, result)
32+
}
33+
34+
func TestFoldRightDivision(t *testing.T) {
35+
// FoldRight with float: 2.0 / (4.0 / (8.0 / 1.0)) = 2.0 / (4.0 / 8.0) = 2.0 / 0.5 = 4.0
36+
nums := []float64{2.0, 4.0, 8.0}
37+
result := u.FoldRight(nums, 1.0, func(n, acc float64) float64 { return n / acc })
38+
assert.Equal(t, 4.0, result)
39+
}
40+
41+
func TestFoldRightStrings(t *testing.T) {
42+
words := []string{"a", "b", "c"}
43+
result := u.FoldRight(words, "", func(s, acc string) string { return s + acc })
44+
assert.Equal(t, "abc", result)
45+
}
46+
47+
func TestFoldRightVsReduce(t *testing.T) {
48+
nums := []int{1, 2, 3}
49+
50+
// Reduce (left fold): (0 - 1) - 2 - 3 = -6
51+
reduceResult := u.Reduce(nums, func(n, acc int) int { return acc - n }, 0)
52+
assert.Equal(t, -6, reduceResult)
53+
54+
// FoldRight: 1 - (2 - (3 - 0)) = 1 - (2 - 3) = 1 - (-1) = 2
55+
foldRightResult := u.FoldRight(nums, 0, func(n, acc int) int { return n - acc })
56+
assert.Equal(t, 2, foldRightResult)
57+
58+
// They should be different for non-associative operations
59+
assert.NotEqual(t, reduceResult, foldRightResult)
60+
}
61+
62+
func TestFoldRightBuildList(t *testing.T) {
63+
nums := []int{1, 2, 3}
64+
result := u.FoldRight(nums, []int{}, func(n int, acc []int) []int {
65+
return append([]int{n}, acc...)
66+
})
67+
assert.Equal(t, []int{1, 2, 3}, result)
68+
}
69+
70+
func BenchmarkFoldRight(b *testing.B) {
71+
nums := make([]int, 1000)
72+
for i := range nums {
73+
nums[i] = i
74+
}
75+
76+
b.ResetTimer()
77+
for i := 0; i < b.N; i++ {
78+
u.FoldRight(nums, 0, func(n, acc int) int { return n + acc })
79+
}
80+
}

0 commit comments

Comments
 (0)