mirror of
https://github.com/aykhans/go-utils.git
synced 2025-10-15 18:25:57 +00:00
93 lines
2.5 KiB
Go
93 lines
2.5 KiB
Go
package slice
|
|
|
|
import (
|
|
"math/rand/v2"
|
|
"time"
|
|
)
|
|
|
|
// Cycle returns a function that cycles through the provided items infinitely.
|
|
// Each call to the returned function returns the next item in sequence, wrapping
|
|
// back to the first item after the last one is returned.
|
|
//
|
|
// If no items are provided, the returned function will always return the zero
|
|
// value for type T.
|
|
//
|
|
// The returned function is not safe for concurrent use. If you need to call it
|
|
// from multiple goroutines, you must synchronize access with a mutex or similar.
|
|
//
|
|
// Example:
|
|
//
|
|
// next := Cycle(1, 2, 3)
|
|
// fmt.Println(next()) // 1
|
|
// fmt.Println(next()) // 2
|
|
// fmt.Println(next()) // 3
|
|
// fmt.Println(next()) // 1
|
|
func Cycle[T any](items ...T) func() T {
|
|
if len(items) == 0 {
|
|
var zero T
|
|
return func() T { return zero }
|
|
}
|
|
|
|
index := 0
|
|
return func() T {
|
|
item := items[index]
|
|
index = (index + 1) % len(items)
|
|
return item
|
|
}
|
|
}
|
|
|
|
// RandomCycle returns a function that cycles through the provided items with
|
|
// randomization. It cycles through all items sequentially, but when it completes
|
|
// a full cycle, it randomly picks a new starting point for the next cycle.
|
|
//
|
|
// The localRand parameter can be used to provide a custom random number generator.
|
|
// If nil, a new generator will be created using the current time as the seed.
|
|
//
|
|
// The returned function is not safe for concurrent use. If you need to call it
|
|
// from multiple goroutines, you must synchronize access with a mutex or similar.
|
|
//
|
|
// Special cases:
|
|
// - If no items are provided, the returned function always returns the zero value for type T.
|
|
// - If only one item is provided, the returned function always returns that item.
|
|
//
|
|
// Example:
|
|
//
|
|
// next := RandomCycle(nil, "a", "b", "c")
|
|
// // Might produce: "b", "c", "a", "c", "a", "b", ...
|
|
// // (cycles through all items, then starts from a random position)
|
|
func RandomCycle[T any](localRand *rand.Rand, items ...T) func() T {
|
|
switch sliceLen := len(items); sliceLen {
|
|
case 0:
|
|
var zero T
|
|
return func() T { return zero }
|
|
case 1:
|
|
return func() T { return items[0] }
|
|
default:
|
|
if localRand == nil {
|
|
//nolint:gosec
|
|
localRand = rand.New(
|
|
rand.NewPCG(
|
|
uint64(time.Now().UnixNano()),
|
|
uint64(time.Now().UnixNano()>>32),
|
|
),
|
|
)
|
|
}
|
|
|
|
currentIndex := localRand.IntN(sliceLen)
|
|
stopIndex := currentIndex
|
|
return func() T {
|
|
item := items[currentIndex]
|
|
currentIndex++
|
|
if currentIndex == sliceLen {
|
|
currentIndex = 0
|
|
}
|
|
if currentIndex == stopIndex {
|
|
currentIndex = localRand.IntN(sliceLen)
|
|
stopIndex = currentIndex
|
|
}
|
|
|
|
return item
|
|
}
|
|
}
|
|
}
|