github.com/haraldrudell/parl@v0.4.176/sets/slice-set.go (about) 1 /* 2 © 2022–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package sets 7 8 import ( 9 "fmt" 10 11 "github.com/haraldrudell/parl/iters" 12 "github.com/haraldrudell/parl/pfmt" 13 "github.com/haraldrudell/parl/plog" 14 ) 15 16 // SliceSet is a set of elements that may not be comparable 17 // - slice index int is used as untyped identity 18 // - elements may be a struct with slice, map or function fields, or 19 // an interface type whose dynamic type is one of those 20 // - if elements have String method, this is description. 21 // Otherwise the “IDn” is description 22 // - — 23 // - SliceSet does: 24 // - — ID range checking 25 // - — provides pointer-iterator 26 // - a set should have: 27 // - — its own unique named type 28 // - — element ID literal, usually a typed const value 29 // - — an iterator 30 // - — a way to determine if a value is valid 31 type SliceSet[E any] struct { 32 elements []E 33 isStringer bool 34 typeName string 35 } 36 37 // NewSliceSet is a slice-based set using slice index as comparable 38 // - element type is therefore any 39 // - if the slice changes, IDs change, too 40 func NewSliceSet[E any](elements []E) (set SetID[int, E]) { 41 var e E 42 var _, isStringer = any(e).(fmt.Stringer) 43 return &SliceSet[E]{ 44 elements: elements, 45 typeName: fmt.Sprintf("%T", e), 46 isStringer: isStringer, 47 } 48 } 49 50 // IsValid returns whether value is part of the set 51 func (s *SliceSet[E]) IsValid(elementID int) (isValid bool) { 52 return elementID >= 0 && elementID < len(s.elements) 53 } 54 55 // Iterator allows iteration over all set elements 56 func (s *SliceSet[E]) Iterator() (iterator iters.Iterator[int]) { 57 if len(s.elements) == 0 { 58 iterator = iters.NewEmptyIterator[int]() 59 return 60 } 61 iterator = iters.NewIntegerIterator(0, len(s.elements)-1) 62 63 return 64 } 65 66 // StringT returns a string representation for an element of this set. 67 // If value is not a valid element, a fmt.Printf value is output like ?'%v' 68 func (s *SliceSet[E]) StringT(anID int) (s2 string) { 69 if anID < 0 || anID >= len(s.elements) { 70 s2 = fmt.Sprintf("?badID‘%d’", anID) 71 return 72 } 73 var value = s.elements[anID] 74 // StringT is intended to be the String method of a named type implementing set. 75 // if StringT method code would somehow invoke the T.String method again, 76 // this will cause infinite recursion and stack overflow panic. 77 s2 = pfmt.NoRecurseVPrint(value) 78 79 return 80 } 81 82 // Description returns a more elaborate string representation 83 // for an element. Description and StringT may return the same value 84 func (s *SliceSet[E]) Description(anID int) (full string) { 85 if anID < 0 || anID >= len(s.elements) { 86 return 87 } 88 var ep = &s.elements[anID] 89 90 // type assert to Element with Description method 91 var elementDescription ElementDescription 92 var ok bool 93 if elementDescription, ok = any(ep).(ElementDescription); !ok { 94 return // not a full element return 95 } 96 97 full = elementDescription.Description() 98 99 return 100 } 101 102 // Element returns the element representation for value or 103 // nil if value is not an element of the set 104 func (s *SliceSet[E]) Element(anID int) (elementType *E) { 105 if anID < 0 || anID >= len(s.elements) { 106 return 107 } 108 elementType = &s.elements[anID] 109 110 return 111 } 112 113 // Iterator allows iteration over all set elements 114 func (s *SliceSet[E]) EIterator() (iterator iters.Iterator[*E]) { 115 return iters.NewSlicePointerIterator(s.elements) 116 } 117 118 func (s *SliceSet[E]) String() (s2 string) { 119 return plog.Sprintf("sliceSet_%T:%d", s.typeName, len(s.elements)) 120 }