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:
		| @@ -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] | ||||
| } | ||||
		Reference in New Issue
	
	Block a user