sliceutil: helpers for mid-slice insert/delete.
Manipulating the hand, deck, etc. is going to use these operations a lot.
This commit is contained in:
parent
99e9e35b1d
commit
20561c574c
@ -31,8 +31,8 @@ func (d *Deck[C]) Len() int {
|
||||
return len(d.cards)
|
||||
}
|
||||
|
||||
// Insert puts a card at a specific location in the Deck. The card previously
|
||||
// at that location and all locations after are shifted one card later.
|
||||
// Insert puts one or more cards at a specific location in the Deck. Cards
|
||||
// at that location and all locations after are shifted deeper into the deck.
|
||||
// Negative indexes are counted from the bottom of the deck. BottomOfDeck is
|
||||
// a sentinel value for the bottommost position; -1 is one card above.
|
||||
//
|
||||
@ -44,7 +44,7 @@ func (d *Deck[C]) Len() int {
|
||||
// WarningTopClamped. Like all warnings, these can be safely ignored and the
|
||||
// program is in a well-defined state, but you may want to check for them
|
||||
// if you expect some other behavior.
|
||||
func (d *Deck[C]) Insert(idx int, card Card[C]) error {
|
||||
func (d *Deck[C]) Insert(idx int, card ...Card[C]) error {
|
||||
var errs ErrorCollector
|
||||
// Calculate actual target index.
|
||||
switch {
|
||||
@ -60,14 +60,7 @@ func (d *Deck[C]) Insert(idx int, card Card[C]) error {
|
||||
idx += d.Len()
|
||||
}
|
||||
// remaining case: 0 <= idx <= d.Len(), which is a normal forward insert index.
|
||||
|
||||
// Place new card on bottom and "bubble" into position.
|
||||
// Takes O(N) time. If this turns out to be a problem, implement a more
|
||||
// efficient data structure.
|
||||
d.cards = append(d.cards, card)
|
||||
for i := len(d.cards) - 1; i > idx; i-- {
|
||||
d.cards[i], d.cards[i-1] = d.cards[i-1], d.cards[i]
|
||||
}
|
||||
d.cards = InsertInto(d.cards, idx, card...)
|
||||
return errs.Emit()
|
||||
}
|
||||
|
||||
|
100
cardsim/sliceutil.go
Normal file
100
cardsim/sliceutil.go
Normal file
@ -0,0 +1,100 @@
|
||||
package cardsim
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// InsertInto inserts one or more items into a slice at an arbitrary index.
|
||||
// Items already in the slice past the target position move to later positons.
|
||||
//
|
||||
// Like `append`, this may move the underlying array and it produces a new
|
||||
// slice header (under the hood, it uses `append`). It returns the new slice
|
||||
// (the original is in an undefined state and should no longer be used).
|
||||
//
|
||||
// If loc is negative or more than one past the end of T, Insert panics.
|
||||
func InsertInto[T any](slice []T, loc int, elements ...T) []T {
|
||||
if loc < 0 || loc > len(slice) {
|
||||
panic(fmt.Sprintf("can't Insert at location %d in %d-element slice", loc, len(slice)))
|
||||
}
|
||||
|
||||
// is this a no-op?
|
||||
if len(elements) == 0 {
|
||||
return slice
|
||||
}
|
||||
// is this just an append?
|
||||
if loc == len(slice) {
|
||||
return append(slice, elements...)
|
||||
}
|
||||
|
||||
offset := len(elements)
|
||||
oldLen := len(slice)
|
||||
newSize := oldLen + offset
|
||||
if newSize <= cap(slice) {
|
||||
// We can reslice in place.
|
||||
slice = slice[:newSize]
|
||||
|
||||
// Scoot trailing to their new positions.
|
||||
copy(slice[loc+offset:], slice[loc:oldLen])
|
||||
|
||||
// Insert the new elements.
|
||||
copy(slice[loc:], elements)
|
||||
|
||||
return slice
|
||||
}
|
||||
|
||||
// Reallocate. Do the normal thing of doubling the size as a minimum
|
||||
// when increasing space for a dynamic array; this amortizes the
|
||||
// cost of repeatedly reallocating and moving the slice.
|
||||
newCap := cap(slice) * 2
|
||||
if newCap < newSize {
|
||||
newCap = newSize
|
||||
}
|
||||
newSlice := make([]T, newSize, newCap)
|
||||
if loc > 0 {
|
||||
copy(newSlice, slice[0:loc])
|
||||
}
|
||||
copy(newSlice[loc:], elements)
|
||||
copy(newSlice[loc+offset:], slice[loc:])
|
||||
return newSlice
|
||||
}
|
||||
|
||||
// DeleteFrom deletes an item from a slice at an arbitrary index. Items after it
|
||||
// scoot up to close the gap. This returns the modified slice (like Append).
|
||||
//
|
||||
// If the provided location is not a valid location in the slice, this panics.
|
||||
func DeleteFrom[T any](slice []T, loc int) []T {
|
||||
return DeleteNFrom(slice, loc, 1)
|
||||
}
|
||||
|
||||
// DeleteNFrom deletes N items from a slice at an arbitrary index. Items after
|
||||
// it scoot up to close the gap. This returns the modified slice (like Append).
|
||||
//
|
||||
// If the range of items that would be deleted is not entirely valid within the
|
||||
// slice, this panics.
|
||||
func DeleteNFrom[T any](slice []T, loc, n int) []T {
|
||||
if loc < 0 || loc+n > len(slice) {
|
||||
panic(fmt.Sprintf("can't delete %d elements from a %d-element slice at location %d", n, len(slice), loc))
|
||||
}
|
||||
|
||||
// Easy cases.
|
||||
if n == 0 {
|
||||
return slice
|
||||
}
|
||||
if loc == 0 {
|
||||
return slice[n:]
|
||||
}
|
||||
if loc+n == len(slice) {
|
||||
return slice[0:loc]
|
||||
}
|
||||
|
||||
// Is it shorter to move up or move down?
|
||||
if len(slice)-loc-n > loc {
|
||||
// Move forward -- the end is big.
|
||||
copy(slice[n:], slice[:loc])
|
||||
return slice[n:]
|
||||
}
|
||||
// Move backward -- the beginnng is big or they're the same size
|
||||
// (and moving backwards preserves more usable append capacity later).
|
||||
copy(slice[loc:], slice[loc+n:])
|
||||
return slice[:len(slice)-n]
|
||||
}
|
Loading…
Reference in New Issue
Block a user