From 4bb102e32e60d9c466324b9a62f6348f7fef8ff0 Mon Sep 17 00:00:00 2001 From: Harald Rudell Date: Mon, 30 Oct 2023 10:59:49 -0700 Subject: [PATCH] =?UTF-8?q?v0.4.118=20iters=20optimized=20for=20Go=20?= =?UTF-8?q?=E2=80=9Cfor=E2=80=9D=20clause?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 2 +- go.sum | 4 +- if-enum.go | 9 ++-- if-iterator.go | 48 ------------------- iters/base-iterator.go | 84 +++++++++++++++++++++++++-------- iters/converter-iterator.go | 7 +++ iters/delegator_test.go | 36 ++++++++++---- iters/empty-iterator.go | 21 +++++---- iters/empty-iterator_test.go | 24 +++++----- iters/function-iterator.go | 7 +++ iters/if-iterator.go | 67 +++++++++++++++++++------- iters/slice-iterator.go | 42 ++++++++++++++--- iters/slice-pointer-iterator.go | 54 ++++++++++++++++----- mains/go.mod | 2 +- pterm/go.mod | 2 +- sets/elements.go | 28 ++++++++++- sets/set.go | 3 +- sqliter/go.mod | 4 +- sqliter/go.sum | 4 +- watchfs/go.mod | 2 +- yamler/go.mod | 2 +- 21 files changed, 301 insertions(+), 151 deletions(-) delete mode 100644 if-iterator.go diff --git a/go.mod b/go.mod index 49320d1c..80de61f8 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.21.3 require ( github.com/google/btree v1.1.2 - github.com/haraldrudell/parl/yamler v0.4.116 + github.com/haraldrudell/parl/yamler v0.4.117 golang.org/x/exp v0.0.0-20231006140011-7918f672742d golang.org/x/sys v0.13.0 golang.org/x/text v0.13.0 diff --git a/go.sum b/go.sum index 5054b97f..b6c2844d 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/haraldrudell/parl/yamler v0.4.116 h1:bi45DW6izbhntjC93ZQyaNERPiu2SwpkAh4cCyQJDkw= -github.com/haraldrudell/parl/yamler v0.4.116/go.mod h1:2VF8uKY0EV6eTDWQXAowD7liuG/zt8szyoC3eS31NsI= +github.com/haraldrudell/parl/yamler v0.4.117 h1:GCjl9mEvTYaeymWDU+ZGjP8illDcFQJBEQsa1v19LnA= +github.com/haraldrudell/parl/yamler v0.4.117/go.mod h1:VeafJ+gpRUHRPdE4wa3rODro1XKCjL+6gVMo5xY8FVY= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= diff --git a/if-enum.go b/if-enum.go index 3347d74c..1ca69e92 100644 --- a/if-enum.go +++ b/if-enum.go @@ -8,6 +8,7 @@ package parl import ( "fmt" + "github.com/haraldrudell/parl/iters" "golang.org/x/exp/constraints" ) @@ -36,16 +37,16 @@ type KeyEnum[K constraints.Ordered, T any] interface { // K - IsKey(key K) (isKey bool) // IsKey checks whether key maps to an enumerated value - Value(key K) (enum T, err error) // Value looks up an enumerated value by key - KeyIterator() (iterator Iterator[K]) // KeyIterator returns an iterator that iterates over all keys in order of definition + IsKey(key K) (isKey bool) // IsKey checks whether key maps to an enumerated value + Value(key K) (enum T, err error) // Value looks up an enumerated value by key + KeyIterator() (iterator iters.Iterator[K]) // KeyIterator returns an iterator that iterates over all keys in order of definition // T IsValid(enum T) (isEnumValue bool) // IsValid checks whether value is among enumerated values Key(value T) (key K, err error) // Key gets the key value for an enumerated T value ValueAny(value any) (enum T, err error) // ValueAny attempts to convert any value to a T enumerated value - Iterator() (iterator Iterator[T]) // Iterator returns an iterator that iterates over all enumerated values in order of definition + Iterator() (iterator iters.Iterator[T]) // Iterator returns an iterator that iterates over all enumerated values in order of definition Description(enum T) (desc string) // Description gets a descriptive sentence for an enum value StringT(enum T) (s string) // StringT provides a string representation for an enumeration value // Compare compares two T values. diff --git a/if-iterator.go b/if-iterator.go deleted file mode 100644 index 87862bcd..00000000 --- a/if-iterator.go +++ /dev/null @@ -1,48 +0,0 @@ -/* -© 2022–present Harald Rudell (https://haraldrudell.github.io/haraldrudell/) -ISC License -*/ - -package parl - -// Iterator allows traversal of values. -// The iterators in parly.iterator are thread-safe and re-entrant, but generally, this depends -// on the iterator implementation used. -// -// // triple-expression works for Iterator that do not require Cancel -// for iterator := NewIterator(); iterator.HasNext(); { -// v := iterator.SameValue() -// } -// -// // conditional expression can be used with all iterators -// iterator := NewIterator() -// for iterator.HasNext() { -// v := iterator.SameValue() -// } -// if err = iterator.Cancel(); … -type Iterator[T any] interface { - // Next advances to next item and returns it. - // if the next item does exist, value is valid and hasValue is true. - // if no next item exists, value is the data type zero-value and hasValue is false. - Next() (value T, hasValue bool) - // HasNext advances to next item and returns hasValue true if this next item does exists. - HasNext() (hasValue bool) - // NextValue advances to next item and returns it. - // If no next value exists, the data type zero-value is returned. - NextValue() (value T) - // Same returns the same value again. - // If a value does exist, it is returned in value and hasValue is true. - // If a value does not exist, the data type zero-value is returned and hasValue is false. - // If Next, FindNext or HasNext have not been invoked, Same first advances to the first item. - Same() (value T, hasValue bool) - // Has returns true if Same() or SameValue will return items. - // If Next, FindNext or HasNext have not been invoked, Has first advances to the first item. - Has() (hasValue bool) - // SameValue returns the same value again. - // If a value does not exist, the data type zero-value is returned. - // If Next, FindNext or HasNext have not been invoked, SameValue first advances to the first item. - SameValue() (value T) - // Cancel release resources for this iterator. - // Not every iterator requires a Cancel invocation. - Cancel() (err error) -} diff --git a/iters/base-iterator.go b/iters/base-iterator.go index b8613951..c4b87191 100644 --- a/iters/base-iterator.go +++ b/iters/base-iterator.go @@ -105,6 +105,70 @@ func NewBaseIterator[T any]( return &BaseIterator[T]{baseIterator: &i} } +// Cond implements the condition statement of a Go “for” clause +// - the iterationVariable is updated by being provided as a pointer. +// iterationVariable cannot be nil +// - errp is an optional error pointer receiving any errors during iterator execution +// - condition is true if iterationVariable was assigned a value and the iteration should continue +func (i *baseIterator[T]) Cond(iterationVariablep *T, errp ...*error) (condition bool) { + if iterationVariablep == nil { + perrors.NewPF("iterationVariablep cannot bee nil") + } + + // handle error + if ep := i.err.Load(); ep != nil { + if err := *ep; err != nil { + if len(errp) > 0 { + if errp0 := errp[0]; errp0 != nil { + *errp0 = err // update errp with error + } + } + return // error return: cond false, iterationVariablep unchanged, errp updated + } + } + + // check for next value + var value T + if value, condition = i.delegateAction(IsNext); condition { + *iterationVariablep = value + } + + return // condition and iterationVariablep updated, errp unchanged +} + +// Cancel release resources for this iterator. +// Not every iterator requires a Cancel invocation. +func (i *baseIterator[T]) Cancel(errp ...*error) (err error) { + + // ignore if cancel alread invoked + if !i.cancelState.CompareAndSwap(uint32(notCanceled), uint32(cancelRequested)) { + i.errWait.Wait() + if err = *i.err.Load(); err != nil { + if len(errp) > 0 { + if ep := errp[0]; ep != nil { + *ep = perrors.AppendError(*ep, err) + } + } + } + return // already beyond cancel + } + + // wait for access to fn + i.publicsLock.Lock() + defer i.publicsLock.Unlock() + + _, _, err = i.enqueueForFn(enqueueForFnCancel) + if err != nil { + if len(errp) > 0 { + if ep := errp[0]; ep != nil { + *ep = perrors.AppendError(*ep, err) + } + } + } + + return +} + // delegateAction finds the next or the same value // - isSame true means first or same value should be returned // - value is the sought value or the T type’s zero-value if no value exists @@ -162,26 +226,6 @@ func (i *baseIterator[T]) delegateAction(isSame NextAction) (value T, hasValue b return // hasValue true, valid value return } -// Cancel release resources for this iterator. -// Not every iterator requires a Cancel invocation. -func (i *baseIterator[T]) Cancel() (err error) { - - // ignore if cancel alread invoked - if !i.cancelState.CompareAndSwap(uint32(notCanceled), uint32(cancelRequested)) { - i.errWait.Wait() - err = *i.err.Load() - return // already beyond cancel - } - - // wait for access to fn - i.publicsLock.Lock() - defer i.publicsLock.Unlock() - - _, _, err = i.enqueueForFn(enqueueForFnCancel) - - return -} - // enqueueForFn invokes iter.fn // - is hasValue true, value is valid, err nil, cancelState notCanceled // - otherwise, i.err is updated, i.errWait released, diff --git a/iters/converter-iterator.go b/iters/converter-iterator.go index cb60980f..20084800 100644 --- a/iters/converter-iterator.go +++ b/iters/converter-iterator.go @@ -73,6 +73,13 @@ func NewConverterIterator[K constraints.Ordered, V any]( } } +// Init implements the right-hand side of a short variable declaration in +// the init statement for a Go “for” clause +func (i *ConverterIterator[K, T]) Init() (iterationVariable T, iterator Iterator[T]) { + iterator = i + return +} + // invokeConverterFunction invokes converterFunction recovering a possible panic // - if cancelState == notCanceled, a new value is requested. // Otherwise, iteration cancel is requested diff --git a/iters/delegator_test.go b/iters/delegator_test.go index 143fbe1e..4df02e68 100644 --- a/iters/delegator_test.go +++ b/iters/delegator_test.go @@ -11,24 +11,42 @@ func TestDelegator(t *testing.T) { slice := []int{5, 6, 7, 8} var actualT int + var value int + var hasNext bool iter := NewSliceIterator(slice) // methods - if !iter.HasNext() { + value, hasNext = iter.Next() + if !hasNext { t.Error("HasNext false") } - - if actualT = iter.NextValue(); actualT != slice[1] { - t.Errorf("NextValue: %d exp %d", actualT, slice[1]) + if value != slice[0] { + t.Errorf("NextValue: %d exp %d", actualT, slice[0]) } - if !iter.Has() { - t.Error("Has false") + value, hasNext = iter.Same() + if !hasNext { + t.Error("HasNext false") } - - if actualT = iter.SameValue(); actualT != slice[1] { - t.Errorf("SameValue: %d exp %d", actualT, slice[1]) + if value != slice[0] { + t.Errorf("NextValue: %d exp %d", actualT, slice[0]) } + + // if !iter.HasNext() { + // t.Error("HasNext false") + // } + + // if actualT = iter.NextValue(); actualT != slice[1] { + // t.Errorf("NextValue: %d exp %d", actualT, slice[1]) + // } + + // if !iter.Has() { + // t.Error("Has false") + // } + + // if actualT = iter.SameValue(); actualT != slice[1] { + // t.Errorf("SameValue: %d exp %d", actualT, slice[1]) + // } } diff --git a/iters/empty-iterator.go b/iters/empty-iterator.go index ce7ac2e6..94df70b5 100644 --- a/iters/empty-iterator.go +++ b/iters/empty-iterator.go @@ -10,11 +10,16 @@ type EmptyIterator[T any] struct{} // NewEmptyIterator returns an empty iterator of values type T. // - EmptyIterator is thread-safe. -func NewEmptyIterator[T any]() (iterator Iterator[T]) { return &EmptyIterator[T]{} } -func (iter *EmptyIterator[T]) Next() (value T, hasValue bool) { return } -func (iter *EmptyIterator[T]) HasNext() (ok bool) { return } -func (iter *EmptyIterator[T]) NextValue() (value T) { return } -func (iter *EmptyIterator[T]) Same() (value T, hasValue bool) { return } -func (iter *EmptyIterator[T]) Has() (hasValue bool) { return } -func (iter *EmptyIterator[T]) SameValue() (value T) { return } -func (iter *EmptyIterator[T]) Cancel() (err error) { return } +func NewEmptyIterator[T any]() (iterator Iterator[T]) { return &EmptyIterator[T]{} } +func (i *EmptyIterator[T]) Init() (iterationVariable T, iterator Iterator[T]) { + iterator = i + return +} +func (i *EmptyIterator[T]) Cond(iterationVariablep *T, errp ...*error) (condition bool) { return } +func (i *EmptyIterator[T]) Next() (value T, hasValue bool) { return } +func (i *EmptyIterator[T]) HasNext() (ok bool) { return } +func (i *EmptyIterator[T]) NextValue() (value T) { return } +func (i *EmptyIterator[T]) Same() (value T, hasValue bool) { return } +func (i *EmptyIterator[T]) Has() (hasValue bool) { return } +func (i *EmptyIterator[T]) SameValue() (value T) { return } +func (i *EmptyIterator[T]) Cancel(errp ...*error) (err error) { return } diff --git a/iters/empty-iterator_test.go b/iters/empty-iterator_test.go index bea20a43..b969067a 100644 --- a/iters/empty-iterator_test.go +++ b/iters/empty-iterator_test.go @@ -22,23 +22,23 @@ func TestEmptyIterator(t *testing.T) { } else if actual != zeroValue { t.Error("Next returned other than zero-value") } - if ok = iter.HasNext(); ok { - t.Error("GoNext returned true") - } - if actual = iter.NextValue(); actual != zeroValue { - t.Errorf("Next not zero value: %d exp %d", actual, zeroValue) - } + // if ok = iter.HasNext(); ok { + // t.Error("GoNext returned true") + // } + // if actual = iter.NextValue(); actual != zeroValue { + // t.Errorf("Next not zero value: %d exp %d", actual, zeroValue) + // } if actual, ok = iter.Same(); ok { t.Error("Next returned true") } else if actual != zeroValue { t.Error("Next returned other than zero-value") } - if ok = iter.Has(); ok { - t.Error("Has returned true") - } - if actual = iter.SameValue(); actual != zeroValue { - t.Errorf("Same not zero value: %d exp %d", actual, zeroValue) - } + // if ok = iter.Has(); ok { + // t.Error("Has returned true") + // } + // if actual = iter.SameValue(); actual != zeroValue { + // t.Errorf("Same not zero value: %d exp %d", actual, zeroValue) + // } if err = iter.Cancel(); err != nil { t.Errorf("Cancel err: %v", err) } diff --git a/iters/function-iterator.go b/iters/function-iterator.go index 5a85ad22..0594dd1f 100644 --- a/iters/function-iterator.go +++ b/iters/function-iterator.go @@ -62,6 +62,13 @@ func NewFunctionIterator[T any]( } } +// Init implements the right-hand side of a short variable declaration in +// the init statement for a Go “for” clause +func (i *FunctionIterator[T]) Init() (iterationVariable T, iterator Iterator[T]) { + iterator = i + return +} + // invokeFn invokes fn recovering a possible panic // - if cancelState == notCanceled, a new value is requested. // Otherwise, iteration cancel is requested diff --git a/iters/if-iterator.go b/iters/if-iterator.go index 7359436a..0a47113b 100644 --- a/iters/if-iterator.go +++ b/iters/if-iterator.go @@ -6,6 +6,12 @@ All rights reserved package iters // Iterator allows traversal of values. +// +// Iterators in the iters package are thread-safe but generally, +// thread-safety depends on the iterator implementation used. +// +// the ForIterator interface is optimized for use in the Go “for” clause +// // The iterators in parly.iterator are thread-safe and re-entrant, but generally, this depends // on the iterator implementation used. // @@ -20,32 +26,61 @@ package iters // v := iterator.SameValue() // } // if err = iterator.Cancel(); … +// +// ForIterator is an iterator optimized for the Init and Condition +// statements of a Go “for” clause +// +// Usage: +// +// for i, iterator := NewIterator().Init(); iterator.Cond(&i); { +// println(i) +// +// var err error +// for i, iterator := NewIterator().Init(); iterator.Cond(&i, &err); { +// } +// if err != nil { +// +// var err error +// var iterator = NewIterator() +// for i, iterator := NewIterator().Init(); iterator.Cond(&i); { +// } +// if err := iterator.Cancel() { +// println(i) type Iterator[T any] interface { + // Init implements the right-hand side of a short variable declaration in + // the init statement for a Go “for” clause + Init() (iterationVariable T, iterator Iterator[T]) + // Cond implements the condition statement of a Go “for” clause + // - the iterationVariable is updated by being provided as a pointer. + // iterationVariable cannot be nil + // - errp is an optional error pointer receiving any errors during iterator execution + // - condition is true if iterationVariable was assigned a value and the iteration should continue + Cond(iterationVariablep *T, errp ...*error) (condition bool) // Next advances to next item and returns it // - if the next item does exist, value is valid and hasValue is true // - if no next item exists, value is the data type zero-value and hasValue is false Next() (value T, hasValue bool) - // HasNext advances to next item and returns hasValue true if this next item does exists - HasNext() (hasValue bool) - // NextValue advances to next item and returns it. - // - If no next value exists, the data type zero-value is returned - NextValue() (value T) // Same returns the same value again. // - If a value does exist, it is returned in value and hasValue is true // - If a value does not exist, the data type zero-value is returned and hasValue is false - // - If Next, FindNext or HasNext have not been invoked, Same first advances to the first item + // - If Next or Cond has not been invoked, Same first advances to the first item Same() (value T, hasValue bool) - // Has returns true if Same() or SameValue will return items. - // - If Next, FindNext or HasNext have not been invoked, - // Has first advances to the first item - Has() (hasValue bool) - // SameValue returns the same value again - // - If a value does not exist, the data type zero-value is returned - // - If Next, FindNext or HasNext have not been invoked, - // SameValue first advances to the first item - SameValue() (value T) // Cancel release resources for this iterator // - returns the first error that occurred during iteration if any // - Not every iterator requires a Cancel invocation - Cancel() (err error) + Cancel(errp ...*error) (err error) + // // HasNext advances to next item and returns hasValue true if this next item does exists + // HasNext() (hasValue bool) + // // NextValue advances to next item and returns it. + // // - If no next value exists, the data type zero-value is returned + // NextValue() (value T) + // // Has returns true if Same() or SameValue will return items. + // // - If Next, FindNext or HasNext have not been invoked, + // // Has first advances to the first item + // Has() (hasValue bool) + // // SameValue returns the same value again + // // - If a value does not exist, the data type zero-value is returned + // // - If Next, FindNext or HasNext have not been invoked, + // // SameValue first advances to the first item + // SameValue() (value T) } diff --git a/iters/slice-iterator.go b/iters/slice-iterator.go index a95178b5..3be990c4 100644 --- a/iters/slice-iterator.go +++ b/iters/slice-iterator.go @@ -8,6 +8,8 @@ package iters import ( "sync" "sync/atomic" + + "github.com/haraldrudell/parl/perrors" ) // SliceIterator traverses a slice container. thread-safe @@ -44,6 +46,39 @@ func NewSliceIterator[T any](slice []T) (iterator Iterator[T]) { return &i } +// Init implements the right-hand side of a short variable declaration in +// the init statement for a Go “for” clause +func (i *SliceIterator[T]) Init() (iterationVariable T, iterator Iterator[T]) { + iterator = i + return +} + +// Cond implements the condition statement of a Go “for” clause +// - the iterationVariable is updated by being provided as a pointer. +// iterationVariable cannot be nil +// - errp is an optional error pointer receiving any errors during iterator execution +// - condition is true if iterationVariable was assigned a value and the iteration should continue +func (i *SliceIterator[T]) Cond(iterationVariablep *T, errp ...*error) (condition bool) { + if iterationVariablep == nil { + perrors.NewPF("iterationVariablep cannot bee nil") + } + + // check for next value + var value T + if value, condition = i.delegateAction(IsNext); condition { + *iterationVariablep = value + } + + return // condition and iterationVariablep updated, errp unchanged +} + +// Cancel release resources for this iterator. Thread-safe +// - not every iterator requires a Cancel invocation +func (i *SliceIterator[T]) Cancel(errp ...*error) (err error) { + i.isEnd.CompareAndSwap(false, true) + return +} + // delegateAction finds the next or the same value. Thread-safe // - isSame == IsSame means first or same value should be returned // - value is the sought value or the T type’s zero-value if no value exists @@ -94,10 +129,3 @@ func (i *SliceIterator[T]) delegateAction(isSame NextAction) (value T, hasValue return // value and hasValue indicates availability } - -// Cancel release resources for this iterator. Thread-safe -// - not every iterator requires a Cancel invocation -func (i *SliceIterator[T]) Cancel() (err error) { - i.isEnd.CompareAndSwap(false, true) - return -} diff --git a/iters/slice-pointer-iterator.go b/iters/slice-pointer-iterator.go index c8f07cd2..3c35b727 100644 --- a/iters/slice-pointer-iterator.go +++ b/iters/slice-pointer-iterator.go @@ -8,14 +8,16 @@ package iters import ( "sync" "sync/atomic" + + "github.com/haraldrudell/parl/perrors" ) // SlicePointerIterator traverses a slice container using pointers to value. thread-safe. // - the difference is that: // - instead of copying a value from the slice, // - a pointer to the slice value is returned -type SlicePointerIterator[T any] struct { - slice []T // the slice providing values +type SlicePointerIterator[E any, T *E] struct { + slice []E // the slice providing values // isEnd is fast outside-lock check for no values available isEnd atomic.Bool @@ -36,7 +38,7 @@ type SlicePointerIterator[T any] struct { // - Next HasNext NextValue // Same Has SameValue // - the delegate provides DelegateAction[T] function - Delegator[*T] + Delegator[T] } // NewSlicePointerIterator returns an iterator of pointers to T @@ -45,17 +47,50 @@ type SlicePointerIterator[T any] struct { // - a pointer to the slice value is returned // - the returned [Iterator] value cannot be copied, the pointer value // must be used -func NewSlicePointerIterator[T any](slice []T) (iterator Iterator[*T]) { - i := SlicePointerIterator[T]{slice: slice} +func NewSlicePointerIterator[E any](slice []E) (iterator Iterator[*E]) { + i := SlicePointerIterator[E, *E]{slice: slice} i.Delegator = *NewDelegator(i.delegateAction) return &i } +// Init implements the right-hand side of a short variable declaration in +// the init statement for a Go “for” clause +func (i *SlicePointerIterator[E, T]) Init() (iterationVariable T, iterator Iterator[T]) { + iterator = i + return +} + +// Cond implements the condition statement of a Go “for” clause +// - the iterationVariable is updated by being provided as a pointer. +// iterationVariable cannot be nil +// - errp is an optional error pointer receiving any errors during iterator execution +// - condition is true if iterationVariable was assigned a value and the iteration should continue +func (i *SlicePointerIterator[E, T]) Cond(iterationVariablep *T, errp ...*error) (condition bool) { + if iterationVariablep == nil { + perrors.NewPF("iterationVariablep cannot bee nil") + } + + // check for next value + var value T + if value, condition = i.delegateAction(IsNext); condition { + *iterationVariablep = value + } + + return // condition and iterationVariablep updated, errp unchanged +} + +// Cancel release resources for this iterator. Thread-safe +// - not every iterator requires a Cancel invocation +func (i *SlicePointerIterator[E, T]) Cancel(errp ...*error) (err error) { + i.isEnd.CompareAndSwap(false, true) + return +} + // Next finds the next or the same value. Thread-safe // - isSame true means first or same value should be returned // - value is the sought value or the T type’s zero-value if no value exists // - hasValue true means value was assigned a valid T value -func (i *SlicePointerIterator[T]) delegateAction(isSame NextAction) (value *T, hasValue bool) { +func (i *SlicePointerIterator[E, T]) delegateAction(isSame NextAction) (value T, hasValue bool) { if i.isEnd.Load() { return // no more values return @@ -101,10 +136,3 @@ func (i *SlicePointerIterator[T]) delegateAction(isSame NextAction) (value *T, h return // value and hasValue indicates availability } - -// Cancel release resources for this iterator. Thread-safe -// - not every iterator requires a Cancel invocation -func (i *SlicePointerIterator[T]) Cancel() (err error) { - i.isEnd.CompareAndSwap(false, true) - return -} diff --git a/mains/go.mod b/mains/go.mod index 49c18c57..9347f757 100644 --- a/mains/go.mod +++ b/mains/go.mod @@ -7,7 +7,7 @@ toolchain go1.21.3 replace github.com/haraldrudell/parl => ../../parl require ( - github.com/haraldrudell/parl v0.4.116 + github.com/haraldrudell/parl v0.4.117 golang.org/x/sys v0.13.0 ) diff --git a/pterm/go.mod b/pterm/go.mod index 189d0b10..55ed1fa5 100644 --- a/pterm/go.mod +++ b/pterm/go.mod @@ -5,7 +5,7 @@ go 1.21 replace github.com/haraldrudell/parl => ../../parl require ( - github.com/haraldrudell/parl v0.4.116 + github.com/haraldrudell/parl v0.4.117 golang.org/x/term v0.13.0 ) diff --git a/sets/elements.go b/sets/elements.go index 867e119d..c33438b7 100644 --- a/sets/elements.go +++ b/sets/elements.go @@ -102,6 +102,32 @@ func NewElements[T comparable, E any](elements []E) (iter iters.Iterator[Element } } +// Init implements the right-hand side of a short variable declaration in +// the init statement for a Go “for” clause +func (i *Elements[T, E]) Init() (iterationVariable Element[T], iterator iters.Iterator[Element[T]]) { + iterator = i + return +} + +// Cond implements the condition statement of a Go “for” clause +// - the iterationVariable is updated by being provided as a pointer. +// iterationVariable cannot be nil +// - errp is an optional error pointer receiving any errors during iterator execution +// - condition is true if iterationVariable was assigned a value and the iteration should continue +func (i *Elements[T, E]) Cond(iterationVariablep *Element[T], errp ...*error) (condition bool) { + if iterationVariablep == nil { + perrors.NewPF("iterationVariablep cannot bee nil") + } + + // check for next value + var value Element[T] + if value, condition = i.delegateAction(iters.IsNext); condition { + *iterationVariablep = value + } + + return // condition and iterationVariablep updated, errp unchanged +} + // delegateAction finds the next or the same value. Thread-safe // - isSame == IsSame means first or same value should be returned // - value is the sought value or the T type’s zero-value if no value exists @@ -158,7 +184,7 @@ func (i *elementsAction[T, E]) delegateAction(isSame iters.NextAction) (value El // Cancel release resources for this iterator. Thread-safe // - not every iterator requires a Cancel invocation -func (i *elementsAction[T, E]) Cancel() (err error) { +func (i *elementsAction[T, E]) Cancel(errp ...*error) (err error) { i.noValuesAvailable.CompareAndSwap(false, true) return } diff --git a/sets/set.go b/sets/set.go index e026a84e..6e604300 100644 --- a/sets/set.go +++ b/sets/set.go @@ -38,8 +38,7 @@ type SetImpl[T comparable] struct { // })) func NewSet[T comparable](elements iters.Iterator[Element[T]]) (set Set[T]) { s := SetImpl[T]{elementMap: map[T]Element[T]{}} - for ; elements.Has(); elements.Next() { - element := elements.SameValue() + for element, _ := elements.Init(); elements.Cond(&element); { valueT := element.Value() if existingElement, ok := s.elementMap[valueT]; ok { panic(perrors.ErrorfPF( diff --git a/sqliter/go.mod b/sqliter/go.mod index f3119819..9fb19f59 100644 --- a/sqliter/go.mod +++ b/sqliter/go.mod @@ -8,7 +8,7 @@ replace github.com/haraldrudell/parl => ../../parl require ( github.com/google/uuid v1.4.0 - github.com/haraldrudell/parl v0.4.116 + github.com/haraldrudell/parl v0.4.117 modernc.org/sqlite v1.26.0 ) @@ -26,7 +26,7 @@ require ( lukechampine.com/uint128 v1.3.0 // indirect modernc.org/cc/v3 v3.41.0 // indirect modernc.org/ccgo/v3 v3.16.15 // indirect - modernc.org/libc v1.28.0 // indirect + modernc.org/libc v1.29.0 // indirect modernc.org/mathutil v1.6.0 // indirect modernc.org/memory v1.7.2 // indirect modernc.org/opt v0.1.3 // indirect diff --git a/sqliter/go.sum b/sqliter/go.sum index 833ab98e..650beba2 100644 --- a/sqliter/go.sum +++ b/sqliter/go.sum @@ -41,8 +41,8 @@ modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v1.28.0 h1:kHB6LtDBV8DEAK7aZT1vWvP92abW9fb8cjb1P9UTpUE= -modernc.org/libc v1.28.0/go.mod h1:DaG/4Q3LRRdqpiLyP0C2m1B8ZMGkQ+cCgOIjEtQlYhQ= +modernc.org/libc v1.29.0 h1:tTFRFq69YKCF2QyGNuRUQxKBm1uZZLubf6Cjh/pVHXs= +modernc.org/libc v1.29.0/go.mod h1:DaG/4Q3LRRdqpiLyP0C2m1B8ZMGkQ+cCgOIjEtQlYhQ= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= diff --git a/watchfs/go.mod b/watchfs/go.mod index a5c0fa70..ed5a20f6 100644 --- a/watchfs/go.mod +++ b/watchfs/go.mod @@ -7,7 +7,7 @@ replace github.com/haraldrudell/parl => ../../parl require ( github.com/fsnotify/fsnotify v1.7.0 github.com/google/uuid v1.4.0 - github.com/haraldrudell/parl v0.4.116 + github.com/haraldrudell/parl v0.4.117 ) require ( diff --git a/yamler/go.mod b/yamler/go.mod index 9b19b055..e6743842 100644 --- a/yamler/go.mod +++ b/yamler/go.mod @@ -7,7 +7,7 @@ replace github.com/haraldrudell/parl => ../../parl replace github.com/haraldrudell/parl/mains => ../mains require ( - github.com/haraldrudell/parl v0.4.116 + github.com/haraldrudell/parl v0.4.117 golang.org/x/exp v0.0.0-20231006140011-7918f672742d gopkg.in/yaml.v3 v3.0.1 )