Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Map/Reduce Methods #7

Merged
merged 2 commits into from
Dec 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions algorithm/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,21 @@ func UniqueBy[T any, K cmp.Ordered, A ~[]T](arr A, getKey func(item T) K) A {
return arr
}

// Sum 求和
func Sum[T cmp.Number](arr []T) T {
var sum T
return Reduce(arr, sum, func(s T, item T) T { return s + item })
}

// Reduce 对数组中的每个元素按序执行一个提供的 reducer 函数,每一次运行 reducer 会将先前元素的计算结果作为参数传入,
// 最后将其结果汇总为单个返回值。
func Reduce[T any, S any](arr []T, initialValue S, reducer func(s S, item T) S) S {
for _, item := range arr {
initialValue = reducer(initialValue, item)
}
return initialValue
}

// Reverse 反转数组
func Reverse[T any, A ~[]T](arr A) A {
var n = len(arr)
Expand Down
27 changes: 27 additions & 0 deletions algorithm/helper_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package algorithm

import (
"github.com/lxzan/dao/hashmap"
"github.com/lxzan/dao/internal/utils"
"github.com/stretchr/testify/assert"
"math/rand"
Expand Down Expand Up @@ -182,3 +183,29 @@ func TestIsZero(t *testing.T) {
assert.False(t, IsZero(1))
assert.False(t, IsZero(" "))
}

func TestReduce(t *testing.T) {
t.Run("", func(t *testing.T) {
var arr = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
var sum = Reduce(arr, 0, func(summarize int, item int) int {
return summarize + item
})
assert.Equal(t, sum, 55)
})

t.Run("", func(t *testing.T) {
var arr = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
var m = hashmap.New[int, struct{}](10)
Reduce(arr, m, func(s hashmap.HashMap[int, struct{}], item int) hashmap.HashMap[int, struct{}] {
s.Set(item, struct{}{})
return s
})
assert.ElementsMatch(t, m.Keys(), []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
})
}

func TestSum(t *testing.T) {
assert.Equal(t, Sum([]int{}), 0)
assert.Equal(t, Sum([]int{1, 3, 5, 7, 9}), 25)
assert.Equal(t, Sum([]uint32{1, 3, 5, 7, 9}), uint32(25))
}
62 changes: 59 additions & 3 deletions heap/heap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,72 @@ package heap

import (
"fmt"
"github.com/lxzan/dao/algorithm"
"github.com/lxzan/dao/internal/utils"
"github.com/lxzan/dao/types/cmp"
"github.com/stretchr/testify/assert"
"math/rand"
"sort"
"testing"
"unsafe"
)

func desc[T cmp.Ordered](a, b T) bool {
return a > b
func compareDesc[T cmp.Ordered](x, y T) int { return -1 * cmp.Compare(x, y) }

func validateHeap[T cmp.Ordered](t *testing.T, h *Heap[T], compare cmp.CompareFunc[T]) {
var n = h.Len()
if n > 0 {
assert.Equal(t, h.data[0], h.Top())
}

var i = 0
h.Range(func(index int, value T) bool {
i++

var base = index << h.bits
var end = algorithm.Min(base+h.ways, n-1)
for j := base + 1; j <= end; j++ {
child := h.data[j]
assert.True(t, compare(value, child) <= 0)
}
return true
})

var keys = make([]T, 0, n)
for h.Len() > 0 {
keys = append(keys, h.Pop())
}
assert.True(t, algorithm.IsSorted(keys, compare))
}

func TestHeap_Random(t *testing.T) {
const count = 10000

var f = func(ways uint32, lessFunc cmp.LessFunc[int], compareFunc cmp.CompareFunc[int]) {
var h = NewWithWays[int](ways, lessFunc)
h.SetCap(count)
for i := 0; i < count; i++ {
flag := rand.Intn(3)
key := rand.Intn(count)
switch flag {
case 0, 1:
h.Push(key)
case 2:
h.Pop()
}
}

validateHeap(t, h, compareFunc)
}

f(2, cmp.Less[int], cmp.Compare[int])
f(2, cmp.Great[int], compareDesc[int])
f(4, cmp.Less[int], cmp.Compare[int])
f(4, cmp.Great[int], compareDesc[int])
f(8, cmp.Less[int], cmp.Compare[int])
f(8, cmp.Great[int], compareDesc[int])
f(16, cmp.Less[int], cmp.Compare[int])
f(16, cmp.Great[int], compareDesc[int])
}

func TestNew(t *testing.T) {
Expand All @@ -36,7 +92,7 @@ func TestNew(t *testing.T) {
}

func TestDesc(t *testing.T) {
var h = NewWithWays(Octal, desc[int])
var h = NewWithWays(Octal, cmp.Great[int])
h.SetCap(8)
h.Push(1)
assert.Equal(t, h.Top(), 1)
Expand Down
139 changes: 60 additions & 79 deletions heap/index_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,90 +11,54 @@ import (
"unsafe"
)

func TestIndexedHeap_Sort(t *testing.T) {
var h = NewIndexedHeap[int, struct{}](Quadratic, nil)
for i := 0; i < 1000; i++ {
h.Push(rand.Int(), struct{}{})
}
var arr []int
for h.Len() > 0 {
arr = append(arr, h.Pop().Key())
func validateIndexedHeap[K cmp.Ordered, V any](t *testing.T, h *IndexedHeap[K, V], compare cmp.CompareFunc[K]) {
var n = h.Len()
if n > 0 {
assert.Equal(t, h.data[0].Key(), h.Top().Key())
}
assert.True(t, algorithm.IsSorted(arr, func(a, b int) int {
if a > b {
return 1
} else if a < b {
return -1
} else {
return 0
}
}))
}

func TestHeap_Random(t *testing.T) {
t.Run("asc", func(t *testing.T) {
const count = 10000
var h = NewIndexedHeap[int, struct{}](Quadratic, cmp.Less[int])
h.SetCap(count)
for i := 0; i < count; i++ {
flag := rand.Intn(5)
key := rand.Intn(count)
switch flag {
case 0, 1:
h.Push(key, struct{}{})
case 2:
h.Pop()
case 3:
n := h.Len()
if n > 0 {
index := rand.Intn(n)
h.UpdateKeyByIndex(index, key)
}
case 4:
n := h.Len()
if n > 0 {
index := rand.Intn(n)
h.DeleteByIndex(index)
}
}
}

for i, item := range h.data {
assert.Equal(t, item.Index(), i)
var i = 0
h.Range(func(ele *Element[K, V]) bool {
assert.Equal(t, ele.Index(), i)
i++

if item.Index() == 0 {
item = h.Top()
}
var n = h.Len()
var base = i << h.bits
var end = algorithm.Min(base+h.ways, n-1)
for j := base + 1; j <= end; j++ {
assert.True(t, h.lessFunc(item.Key(), h.GetByIndex(j).Key()))
}
var base = ele.Index() << h.bits
var end = algorithm.Min(base+h.ways, n-1)
for j := base + 1; j <= end; j++ {
child := h.GetByIndex(j)
assert.True(t, compare(ele.Key(), child.Key()) <= 0)
}
return true
})

t.Run("desc", func(t *testing.T) {
const count = 10000
var h = NewIndexedHeap[int, struct{}](Quadratic, func(a, b int) bool {
return a > b
})
var keys = make([]K, 0, n)
for h.Len() > 0 {
keys = append(keys, h.Pop().Key())
}
assert.True(t, algorithm.IsSorted(keys, compare))
}

func TestIndexedHeap_Random(t *testing.T) {
const count = 10000

var f = func(ways uint32, lessFunc cmp.LessFunc[int], compareFunc cmp.CompareFunc[int]) {
var h = NewIndexedHeap[int, struct{}](ways, lessFunc)
h.SetCap(count)
for i := 0; i < count; i++ {
flag := rand.Intn(5)
flag := utils.Alphabet.Intn(6)
key := rand.Intn(count)
switch flag {
case 0, 1:
case 0, 1, 2:
h.Push(key, struct{}{})
case 2:
h.Pop()
case 3:
h.Pop()
case 4:
n := h.Len()
if n > 0 {
index := rand.Intn(n)
h.UpdateKeyByIndex(index, key)
}
case 4:
case 5:
n := h.Len()
if n > 0 {
index := rand.Intn(n)
Expand All @@ -103,20 +67,37 @@ func TestHeap_Random(t *testing.T) {
}
}

for i, item := range h.data {
assert.Equal(t, item.Index(), i)
validateIndexedHeap(t, h, compareFunc)
}

if item.Index() == 0 {
item = h.Top()
}
var n = h.Len()
var base = i << h.bits
var end = algorithm.Min(base+h.ways, n-1)
for j := base + 1; j <= end; j++ {
assert.True(t, h.lessFunc(item.Key(), h.GetByIndex(j).Key()))
}
f(2, cmp.Less[int], cmp.Compare[int])
f(2, cmp.Great[int], compareDesc[int])
f(4, cmp.Less[int], cmp.Compare[int])
f(4, cmp.Great[int], compareDesc[int])
f(8, cmp.Less[int], cmp.Compare[int])
f(8, cmp.Great[int], compareDesc[int])
f(16, cmp.Less[int], cmp.Compare[int])
f(16, cmp.Great[int], compareDesc[int])
}

func TestIndexedHeap_Sort(t *testing.T) {
var h = NewIndexedHeap[int, struct{}](Quadratic, nil)
for i := 0; i < 1000; i++ {
h.Push(rand.Int(), struct{}{})
}
var arr []int
for h.Len() > 0 {
arr = append(arr, h.Pop().Key())
}
assert.True(t, algorithm.IsSorted(arr, func(a, b int) int {
if a > b {
return 1
} else if a < b {
return -1
} else {
return 0
}
})
}))
}

func TestIndexedHeap_Range(t *testing.T) {
Expand Down
7 changes: 7 additions & 0 deletions internal/utils/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ func (c *RandomString) Generate(n int) string {
return string(b)
}

func (c *RandomString) Intn(n int) int {
c.mu.Lock()
v := c.rand.Intn(n)
c.mu.Unlock()
return v
}

func IsSameSlice[T comparable](a, b []T) bool {
if len(a) != len(b) {
return false
Expand Down
6 changes: 6 additions & 0 deletions internal/utils/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ func TestRandomString_Generate(t *testing.T) {
assert.Equal(t, len(s), 6)
}

func TestRandomString_Intn(t *testing.T) {
for i := 0; i < 100; i++ {
v := Alphabet.Intn(i + 10)
assert.True(t, v < i+10)
}
}
func TestReverseStrings(t *testing.T) {
{
var arr = []string{"a", "b", "c"}
Expand Down
5 changes: 4 additions & 1 deletion types/cmp/cmp.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,12 @@ type (
CompareFunc[T any] func(a, b T) int
)

// Less 比大小函数(升序)
// Less 小于
func Less[T Ordered](x, y T) bool { return x < y }

// Great 大于
func Great[T Ordered](x, y T) bool { return x > y }

// Compare 比较函数(升序)
func Compare[T Ordered](x, y T) int {
if x < y {
Expand Down
Loading