mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-04 12:52:28 +08:00
fix(package): [slice] Fix RigthPadding and LeftPadding (#322)
* Fixes in RightPadding and LeftPadding * Tests cover padding empty, nil, and negative lenght * Implemented repeat, concat, and grow functionalities as internal.
This commit is contained in:
@@ -1463,36 +1463,28 @@ func Random[T any](slice []T) (val T, idx int) {
|
|||||||
return slice[idx], idx
|
return slice[idx], idx
|
||||||
}
|
}
|
||||||
|
|
||||||
// RightPadding adds padding to the right end of a slice.
|
// RightPadding returns a copy of the slice padding the given value to the right end of a slice.
|
||||||
|
// If paddingLength is zero or less, the function returns a copy of the slice.
|
||||||
// Play: https://go.dev/play/p/0_2rlLEMBXL
|
// Play: https://go.dev/play/p/0_2rlLEMBXL
|
||||||
func RightPadding[T any](slice []T, paddingValue T, paddingLength int) []T {
|
func RightPadding[T any](slice []T, paddingValue T, paddingLength int) []T {
|
||||||
if paddingLength == 0 {
|
suffix := []T{}
|
||||||
return slice
|
if paddingLength > 0 {
|
||||||
|
suffix = repeat([]T{paddingValue}, paddingLength)
|
||||||
}
|
}
|
||||||
for i := 0; i < paddingLength; i++ {
|
padded := concat(slice, suffix)
|
||||||
slice = append(slice, paddingValue)
|
return padded
|
||||||
}
|
|
||||||
return slice
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LeftPadding adds padding to the left begin of a slice.
|
// LeftPadding returns a copy of the slice padding the given value to the left begin of a slice.
|
||||||
|
// If paddingLength is zero or less, the function returns a copy of the slice.
|
||||||
// Play: https://go.dev/play/p/jlQVoelLl2k
|
// Play: https://go.dev/play/p/jlQVoelLl2k
|
||||||
func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T {
|
func LeftPadding[T any](slice []T, paddingValue T, paddingLength int) []T {
|
||||||
if paddingLength == 0 {
|
prefix := []T{}
|
||||||
return slice
|
if paddingLength > 0 {
|
||||||
|
prefix = repeat([]T{paddingValue}, paddingLength)
|
||||||
}
|
}
|
||||||
|
padded := concat(prefix, slice)
|
||||||
paddedSlice := make([]T, len(slice)+paddingLength)
|
return padded
|
||||||
i := 0
|
|
||||||
for ; i < paddingLength; i++ {
|
|
||||||
paddedSlice[i] = paddingValue
|
|
||||||
}
|
|
||||||
for j := 0; j < len(slice); j++ {
|
|
||||||
paddedSlice[i] = slice[j]
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
return paddedSlice
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Frequency counts the frequency of each element in the slice.
|
// Frequency counts the frequency of each element in the slice.
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package slice
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/bits"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"golang.org/x/exp/constraints"
|
"golang.org/x/exp/constraints"
|
||||||
@@ -96,3 +97,71 @@ func partitionAnySlice[T any](slice []T, lowIndex, highIndex int, less func(a, b
|
|||||||
func swap[T any](slice []T, i, j int) {
|
func swap[T any](slice []T, i, j int) {
|
||||||
slice[i], slice[j] = slice[j], slice[i]
|
slice[i], slice[j] = slice[j], slice[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `repeat` returns a new slice that repeats the provided slice the given number of
|
||||||
|
// times. The result has length and capacity (len(x) * count). The result is never nil.
|
||||||
|
// Repeat panics if count is negative or if the result of (len(x) * count) overflows.
|
||||||
|
//
|
||||||
|
// repeat has been provided in the standard lib within the package `slices` under the
|
||||||
|
// name Repeat since GO version 1.21 onwards. As lancet commits to compatibility with GO
|
||||||
|
// 1.18 onwards, we implement the functionality as an internal function.
|
||||||
|
func repeat[S ~[]E, E any](x S, count int) S {
|
||||||
|
if count < 0 {
|
||||||
|
panic("count cannot be negative")
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxInt = ^uint(0) >> 1
|
||||||
|
hi, lo := bits.Mul(uint(len(x)), uint(count))
|
||||||
|
if hi > 0 || lo > maxInt {
|
||||||
|
panic("the result of (len(x) * count) overflows")
|
||||||
|
}
|
||||||
|
|
||||||
|
newslice := make(S, int(lo)) // lo = len(x) * count
|
||||||
|
n := copy(newslice, x)
|
||||||
|
for n < len(newslice) {
|
||||||
|
n += copy(newslice[n:], newslice[:n])
|
||||||
|
}
|
||||||
|
return newslice
|
||||||
|
}
|
||||||
|
|
||||||
|
// concat returns a new slice concatenating the passed in slices.
|
||||||
|
//
|
||||||
|
// concat has been provided in the standard lib within the package `slices` under the
|
||||||
|
// name Concat since GO version 1.21 onwards. As lancet commits to compatibility with GO
|
||||||
|
// 1.18 onwards, we implement the functionality as an internal function.
|
||||||
|
func concat[S ~[]E, E any](slices ...S) S {
|
||||||
|
size := 0
|
||||||
|
for _, s := range slices {
|
||||||
|
size += len(s)
|
||||||
|
if size < 0 {
|
||||||
|
panic("len out of range")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Use Grow, not make, to round up to the size class:
|
||||||
|
// the extra space is otherwise unused and helps
|
||||||
|
// callers that append a few elements to the result.
|
||||||
|
newslice := grow[S](nil, size)
|
||||||
|
for _, s := range slices {
|
||||||
|
newslice = append(newslice, s...)
|
||||||
|
}
|
||||||
|
return newslice
|
||||||
|
}
|
||||||
|
|
||||||
|
// grow increases the slice's capacity, if necessary, to guarantee space for
|
||||||
|
// another n elements. After grow(n), at least n elements can be appended
|
||||||
|
// to the slice without another allocation. If n is negative or too large to
|
||||||
|
// allocate the memory, grow panics.
|
||||||
|
//
|
||||||
|
// grow has been provided in the standard lib within the package `slices` under the
|
||||||
|
// name Grow since GO version 1.21 onwards. As lancet commits to compatibility with GO
|
||||||
|
// 1.18 onwards, we implement the functionality as an internal function.
|
||||||
|
func grow[S ~[]E, E any](s S, n int) S {
|
||||||
|
if n < 0 {
|
||||||
|
panic("cannot be negative")
|
||||||
|
}
|
||||||
|
if n -= cap(s) - len(s); n > 0 {
|
||||||
|
// This expression allocates only once.
|
||||||
|
s = append(s[:cap(s)], make([]E, n)...)[:len(s)]
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|||||||
@@ -1756,6 +1756,20 @@ func TestRightPaddingAndLeftPadding(t *testing.T) {
|
|||||||
|
|
||||||
padded := LeftPadding(RightPadding(nums, 0, 3), 0, 3)
|
padded := LeftPadding(RightPadding(nums, 0, 3), 0, 3)
|
||||||
assert.Equal([]int{0, 0, 0, 1, 2, 3, 4, 5, 0, 0, 0}, padded)
|
assert.Equal([]int{0, 0, 0, 1, 2, 3, 4, 5, 0, 0, 0}, padded)
|
||||||
|
|
||||||
|
// Test with negative padding length
|
||||||
|
paddedNegative := LeftPadding(RightPadding(nums, 0, -3), 0, -3)
|
||||||
|
assert.Equal([]int{1, 2, 3, 4, 5}, paddedNegative)
|
||||||
|
|
||||||
|
// Test with empty slice
|
||||||
|
empty := []int{}
|
||||||
|
paddedEmpty := LeftPadding(RightPadding(empty, 0, 3), 0, 3)
|
||||||
|
assert.Equal([]int{0, 0, 0, 0, 0, 0}, paddedEmpty)
|
||||||
|
|
||||||
|
// Test with nil
|
||||||
|
nilSlice := []int(nil)
|
||||||
|
paddedNil := LeftPadding(RightPadding(nilSlice, 0, 3), 0, 3)
|
||||||
|
assert.Equal([]int{0, 0, 0, 0, 0, 0}, paddedNil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUniqueByConcurrent(t *testing.T) {
|
func TestUniqueByConcurrent(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user