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  }