### Install Yaegi Command-Line Executable Source: https://github.com/traefik/yaegi/blob/master/README.md Install the Yaegi command-line tool using 'go install'. Consider using 'rlwrap' for enhanced command-line editing and history. ```bash go install github.com/traefik/yaegi/cmd/yaegi@latest ``` -------------------------------- ### Go OnceFunc Example Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_22_sync_oncefunc.go.txt Demonstrates how to use sync.OnceFunc to ensure a function is called only once. This is useful for one-time initialization. ```go package sync // OnceFunc returns a function that invokes f only once. The returned function // may be called concurrently. // // If f panics, the returned function will panic with the same value on every call. func OnceFunc(f func()) func() { var ( once Once valid bool p any ) // Construct the inner closure just once to reduce costs on the fast path. g := func() { defer func() { p = recover() if !valid { // Re-panic immediately so on the first call the user gets a // complete stack trace into f. panic(p) } }() f() f = nil // Do not keep f alive after invoking it. valid = true // Set only if f does not panic. } return func() { once.Do(g) if !valid { panic(p) } } } ``` -------------------------------- ### Install Yaegi Go Package Source: https://github.com/traefik/yaegi/blob/master/README.md Import the Yaegi interpreter package into your Go project. ```go import "github.com/traefik/yaegi/interp" ``` -------------------------------- ### CI Integration for Yaegi Installation Source: https://github.com/traefik/yaegi/blob/master/README.md Use this curl command to download and execute the Yaegi installation script in your CI environment. Specify the desired version. ```bash curl -sfL https://raw.githubusercontent.com/traefik/yaegi/master/install.sh | bash -s -- -b $GOPATH/bin v0.9.0 ``` -------------------------------- ### Dynamic Extension Framework with Yaegi Source: https://github.com/traefik/yaegi/blob/master/README.md This example demonstrates using Yaegi to interpret Go code dynamically within a compiled program. It shows how to evaluate source code, retrieve symbols, and cast them to their expected types. ```go package main import "github.com/traefik/yaegi/interp" const src = `package foo func Bar(s string) string { return s + "-Foo" }` func main() { i := interp.New(interp.Options{}) _, err := i.Eval(src) if err != nil { panic(err) } v, err := i.Eval("foo.Bar") if err != nil { panic(err) } bar := v.Interface().(func(string) string) r := bar("Kung") println(r) } ``` -------------------------------- ### Go OnceValues Example Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_22_sync_oncefunc.go.txt Demonstrates how to use sync.OnceValues to ensure a function returning multiple values is called only once. The results are cached and returned on subsequent calls. ```go package sync // OnceValues returns a function that invokes f only once and returns the values // returned by f. The returned function may be called concurrently. // // If f panics, the returned function will panic with the same value on every call. func OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) { var ( once Once valid bool p any r1 T1 r2 T2 ) g := func() { defer func() { p = recover() if !valid { panic(p) } }() r1, r2 = f() f = nil valid = true } return func() (T1, T2) { once.Do(g) if !valid { panic(p) } return r1, r2 } } ``` -------------------------------- ### Go OnceValue Example Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_22_sync_oncefunc.go.txt Demonstrates how to use sync.OnceValue to ensure a function returning a value is called only once. The result is cached and returned on subsequent calls. ```go package sync // OnceValue returns a function that invokes f only once and returns the value // returned by f. The returned function may be called concurrently. // // If f panics, the returned function will panic with the same value on every call. func OnceValue[T any](f func() T) func() T { var ( once Once valid bool p any result T ) g := func() { defer func() { p = recover() if !valid { panic(p) } }() result = f() f = nil valid = true } return func() T { once.Do(g) if !valid { panic(p) } return result } } ``` -------------------------------- ### startIdx: Find Needle Start Index in Haystack Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_22_slices_slices.go.txt Returns the starting index of a needle slice within a haystack slice. This function requires that the needle is entirely contained within the haystack's memory range. It panics if the needle is not found. ```go func startIdx[E any](haystack, needle []E) int { p := &needle[0] for i := range haystack { if p == &haystack[i] { return i } } // TODO: what if the overlap is by a non-integral number of Es? panic("needle not found") } ``` -------------------------------- ### Swap Range for Ordered Slices Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_21_slices.go.txt Swaps n elements starting from index a with n elements starting from index b in an ordered slice. ```go func swapRangeOrdered[E cmp.Ordered](data []E, a, b, n int) { for i := 0; i < n; i++ { data[a+i], data[b+i] = data[b+i], data[a+i] } } ``` -------------------------------- ### Find Start Index of Slice Sub-slice in Go Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_21_slices.go.txt Returns the starting index of a 'needle' slice within a 'haystack' slice. Assumes the needle is entirely contained within the haystack's memory. ```go func startIdx[E any](haystack, needle []E) int { p := &needle[0] for i := range haystack { if p == &haystack[i] { return i } } panic("needle not found") } ``` -------------------------------- ### Compare Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_22_slices_slices.go.txt Compare compares the elements of s1 and s2, using [cmp.Compare] on each pair of elements. The elements are compared sequentially, starting at index 0, until one element is not equal to the other. The result of comparing the first non-matching elements is returned. If both slices are equal until one of them ends, the shorter slice is considered less than the longer one. The result is 0 if s1 == s2, -1 if s1 < s2, and +1 if s1 > s2. ```APIDOC ## Compare[S ~[]E, E cmp.Ordered](s1, s2 S) int ### Description Compares the elements of s1 and s2, using [cmp.Compare] on each pair of elements. The elements are compared sequentially, starting at index 0, until one element is not equal to the other. The result of comparing the first non-matching elements is returned. If both slices are equal until one of them ends, the shorter slice is considered less than the longer one. The result is 0 if s1 == s2, -1 if s1 < s2, and +1 if s1 > s2. ### Parameters - **s1** (S) - The first slice. - **s2** (S) - The second slice. ### Returns - **int** - 0 if s1 == s2, -1 if s1 < s2, and +1 if s1 > s2. ``` -------------------------------- ### Swap Slice Segments Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_22_slices_zsortanyfunc.go.txt Swaps 'n' elements starting from index 'a' with 'n' elements starting from index 'b' within a slice. ```go func swapRangeCmpFunc[E any](data []E, a, b, n int, cmp func(a, b E) int) { for i := 0; i < n; i++ { data[a+i], data[b+i] = data[b+i], data[a+i] } } ``` -------------------------------- ### CLI Interpreter with Pre-imported Packages Source: https://github.com/traefik/yaegi/blob/master/README.md Demonstrates using the Yaegi CLI interpreter with pre-imported packages like 'reflect' and 'time'. ```console $ yaegi > reflect.TypeOf(time.Date) : func(int, time.Month, int, int, int, int, int, *time.Location) time.Time > ``` -------------------------------- ### Choose Pivot for Quicksort Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_22_slices_zsortanyfunc.go.txt Chooses a pivot element from a slice segment using different strategies based on the segment length: static pivot for small segments, median-of-three for medium, and Tukey ninther for larger segments. ```go // choosePivotCmpFunc chooses a pivot in data[a:b]. // // [0,8): chooses a static pivot. // [8,shortestNinther): uses the simple median-of-three method. // [shortestNinther,∞): uses the Tukey ninther method. func choosePivotCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) (pivot int, hint sortedHint) { const ( shortestNinther = 50 maxSwaps = 4 * 3 ) l := b - a var ( swaps int i = a + l/4*1 j = a + l/4*2 k = a + l/4*3 ) if l >= 8 { if l >= shortestNinther { // Tukey ninther method, the idea came from Rust's implementation. i = medianAdjacentCmpFunc(data, i, &swaps, cmp) j = medianAdjacentCmpFunc(data, j, &swaps, cmp) k = medianAdjacentCmpFunc(data, k, &swaps, cmp) } // Find the median among i, j, k and stores it into j. j = medianCmpFunc(data, i, j, k, &swaps, cmp) } switch swaps { case 0: return j, increasingHint case maxSwaps: return j, decreasingHint default: return j, unknownHint } } ``` -------------------------------- ### Partition for Generic Slices Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_22_slices_zsortanyfunc.go.txt Performs a single quicksort partition step on a generic slice. Elements less than the pivot are moved to the left, and elements greater than or equal to the pivot are moved to the right. ```go func partitionCmpFunc[E any](data []E, a, b, pivot int, cmp func(a, b E) int) (newpivot int, alreadyPartitioned bool) { data[a], data[pivot] = data[pivot], data[a] i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned for i <= j && (cmp(data[i], data[a]) < 0) { i++ } for i <= j && !(cmp(data[j], data[a]) < 0) { j-- } if i > j { data[j], data[a] = data[a], data[j] return j, true } data[i], data[j] = data[j], data[i] i++ j-- for { for i <= j && (cmp(data[i], data[a]) < 0) { i++ } for i <= j && !(cmp(data[j], data[a]) < 0) { j-- } if i > j { break } data[i], data[j] = data[j], data[i] i++ j-- } data[j], data[a] = data[a], data[j] return j, false } ``` -------------------------------- ### Interpreting Go Packages via CLI Source: https://github.com/traefik/yaegi/blob/master/README.md Execute Go packages, directories, or files using the Yaegi CLI with specific flags like -syscall, -unsafe, and -unrestricted. ```console $ yaegi -syscall -unsafe -unrestricted github.com/traefik/yaegi/cmd/yaegi > ``` -------------------------------- ### Partition Slice Elements (QuickSort) Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_21_slices.go.txt Partitions a slice segment `data[a:b]` around a pivot element `data[a]`. It rearranges elements such that elements less than or equal to the pivot come before elements greater than the pivot. Assumes elements smaller than the pivot are not present in `data[a:b]`. ```go func partitionCmpFunc[E any](data []E, a, b, pivot int, cmp func(a, b E) int) (newpivot int) { data[a], data[pivot] = data[pivot], data[a] i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned for { for i <= j && (cmp(data[a], data[i]) < 0) { i++ } for i <= j && !(cmp(data[a], data[j]) < 0) { j-- } if i > j { break } data[i], data[j] = data[j], data[i] i++ j-- } data[j], data[a] = data[a], data[j] return j, false } ``` -------------------------------- ### Go sync.Once.Do: Method to execute a function once Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_21_sync.go.txt The `Do` method on `sync.Once` calls the provided function `f` if and only if `Do` has not been called before for this `Once` instance. It guarantees that `f` completes before `Do` returns. If `f` panics, `Do` considers it finished, and subsequent calls to `Do` will not execute `f`. ```go // Do calls the function f if and only if Do is being called for the // first time for this instance of Once. In other words, given // // var once Once // // if once.Do(f) is called multiple times, only the first call will invoke f, // even if f has a different value in each invocation. A new instance of // Once is required for each function to execute. // // Do is intended for initialization that must be run exactly once. Since f // is niladic, it may be necessary to use a function literal to capture the // arguments to a function to be invoked by Do: // // config.once.Do(func() { config.init(filename) }) // // Because no call to Do returns until the one call to f returns, if f causes // Do to be called, it will deadlock. // // If f panics, Do considers it to have returned; future calls of Do return // without calling f. func (o *Once) Do(f func()) { // Note: Here is an incorrect implementation of Do: // // if atomic.CompareAndSwapUint32(&o.done, 0, 1) { // f() // } // // Do guarantees that when it returns, f has finished. // This implementation would not implement that guarantee: // given two simultaneous calls, the winner of the cas would // call f, and the second would return immediately, without // waiting for the first's call to f to complete. // This is why the slow path falls back to a mutex, and why // the atomic.StoreUint32 must be delayed until after f returns. if atomic.LoadUint32(&o.done) == 0 { // Outlined slow-path to allow inlining of the fast-path. o.doSlow(f) } } ``` -------------------------------- ### PDQsort Implementation Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_21_slices.go.txt Sorts a slice using a pattern-defeating quicksort algorithm. It falls back to heapsort if too many unbalanced partitions occur. This implementation is based on C++ and Rust versions. ```go // pdqsortCmpFunc sorts data[a:b]. // The algorithm based on pattern-defeating quicksort(pdqsort), but without the optimizations from BlockQuicksort. // pdqsort paper: https://arxiv.org/pdf/2106.05123.pdf // C++ implementation: https://github.com/orlp/pdqsort // Rust implementation: https://docs.rs/pdqsort/latest/pdqsort/ // limit is the number of allowed bad (very unbalanced) pivots before falling back to heapsort. func pdqsortCmpFunc[E any](data []E, a, b, limit int, cmp func(a, b E) int) { const maxInsertion = 12 var ( wasBalanced = true // whether the last partitioning was reasonably balanced wasPartitioned = true // whether the slice was already partitioned ) for { length := b - a if length <= maxInsertion { insertionSortCmpFunc(data, a, b, cmp) return } // Fall back to heapsort if too many bad choices were made. if limit == 0 { heapSortCmpFunc(data, a, b, cmp) return } // If the last partitioning was imbalanced, we need to breaking patterns. if !wasBalanced { breakPatternsCmpFunc(data, a, b, cmp) limit-- } pivot, hint := choosePivotCmpFunc(data, a, b, cmp) if hint == decreasingHint { reverseRangeCmpFunc(data, a, b, cmp) // The chosen pivot was pivot-a elements after the start of the array. // After reversing it is pivot-a elements before the end of the array. // The idea came from Rust's implementation. pivot = (b - 1) - (pivot - a) hint = increasingHint } // The slice is likely already sorted. if wasBalanced && wasPartitioned && hint == increasingHint { if partialInsertionSortCmpFunc(data, a, b, cmp) { return } } // Probably the slice contains many duplicate elements, partition the slice into // elements equal to and elements greater than the pivot. if a > 0 && !(cmp(data[a-1], data[pivot]) < 0) { mid := partitionEqualCmpFunc(data, a, b, pivot, cmp) a = mid continue } mid, alreadyPartitioned := partitionCmpFunc(data, a, b, pivot, cmp) wasPartitioned = alreadyPartitioned leftLen, rightLen := mid-a, b-mid balanceThreshold := length / 8 if leftLen < rightLen { wasBalanced = leftLen >= balanceThreshold pdqsortCmpFunc(data, a, mid, limit, cmp) a = mid + 1 } else { wasBalanced = rightLen >= balanceThreshold pdqsortCmpFunc(data, mid+1, b, limit, cmp) b = mid } } } ``` -------------------------------- ### PDQsort for Generic Slices Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_22_slices_zsortanyfunc.go.txt Sorts a generic slice using a hybrid algorithm based on pattern-defeating quicksort. Falls back to heapsort for performance on pathological cases. ```go func pdqsortCmpFunc[E any](data []E, a, b, limit int, cmp func(a, b E) int) { const maxInsertion = 12 var ( wasBalanced = true // whether the last partitioning was reasonably balanced wasPartitioned = true // whether the slice was already partitioned ) for { length := b - a if length <= maxInsertion { insertionSortCmpFunc(data, a, b, cmp) return } // Fall back to heapsort if too many bad choices were made. if limit == 0 { heapSortCmpFunc(data, a, b, cmp) return } // If the last partitioning was imbalanced, we need to breaking patterns. if !wasBalanced { breakPatternsCmpFunc(data, a, b, cmp) limit-- } pivot, hint := choosePivotCmpFunc(data, a, b, cmp) if hint == decreasingHint { reverseRangeCmpFunc(data, a, b, cmp) // The chosen pivot was pivot-a elements after the start of the array. // After reversing it is pivot-a elements before the end of the array. // The idea came from Rust's implementation. pivot = (b - 1) - (pivot - a) hint = increasingHint } // The slice is likely already sorted. if wasBalanced && wasPartitioned && hint == increasingHint { if partialInsertionSortCmpFunc(data, a, b, cmp) { return } } // Probably the slice contains many duplicate elements, partition the slice into // elements equal to and elements greater than the pivot. if a > 0 && !(cmp(data[a-1], data[pivot]) < 0) { mid := partitionEqualCmpFunc(data, a, b, pivot, cmp) a = mid continue } mid, alreadyPartitioned := partitionCmpFunc(data, a, b, pivot, cmp) wasPartitioned = alreadyPartitioned leftLen, rightLen := mid-a, b-mid balanceThreshold := length / 8 if leftLen < rightLen { wasBalanced = leftLen >= balanceThreshold pdqsortCmpFunc(data, a, mid, limit, cmp) a = mid + 1 } else { wasBalanced = rightLen >= balanceThreshold pdqsortCmpFunc(data, mid+1, b, limit, cmp) b = mid } } } ``` -------------------------------- ### Go sync.Once: Ensure an action is performed exactly once Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_21_sync.go.txt The `sync.Once` type is used to ensure that a particular action or initialization is performed exactly one time, even when called concurrently. It is crucial for safe initialization of shared resources. ```go // Once is an object that will perform exactly one action. // // A Once must not be copied after first use. // // In the terminology of the Go memory model, // the return from f “synchronizes before” // the return from any call of once.Do(f). type Once struct { // done indicates whether the action has been performed. // It is first in the struct because it is used in the hot path. // The hot path is inlined at every call site. // Placing done first allows more compact instructions on some architectures (amd64/386), // and fewer instructions (to calculate offset) on other architectures. done uint32 m Mutex } ``` -------------------------------- ### Quicksort Partition Function Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_21_slices.go.txt Performs a single partition step for the quicksort algorithm. It rearranges elements around a pivot such that elements less than the pivot are to its left and elements greater than or equal to the pivot are to its right. ```go // partitionCmpFunc does one quicksort partition. // Let p = data[pivot] // Moves elements in data[a:b] around, so that data[i]

=p for inewpivot. // On return, data[newpivot] = p func partitionCmpFunc[E any](data []E, a, b, pivot int, cmp func(a, b E) int) (newpivot int, alreadyPartitioned bool) { data[a], data[pivot] = data[pivot], data[a] i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned for i <= j && (cmp(data[i], data[a]) < 0) { i++ } for i <= j && !(cmp(data[j], data[a]) < 0) { j-- } if i > j { data[j], data[a] = data[a], data[j] return j, true } data[i], data[j] = data[j], data[i] i++ j-- ``` -------------------------------- ### Shallow Copy a Map - Go Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_21_maps.go.txt Use Clone to create a shallow copy of a map. New keys and values are assigned directly. Handles nil maps by returning nil. ```go func Clone[M ~map[K]V, K comparable, V any](m M) M { // Preserve nil in case it matters. if m == nil { return nil } return clone(m).(M) } ``` -------------------------------- ### Interactive CLI Interpreter Source: https://github.com/traefik/yaegi/blob/master/README.md Run Yaegi in interactive mode for a REPL experience. Standard library packages are pre-imported, allowing direct usage. ```console $ yaegi > 1 + 2 3 > import "fmt" > fmt.Println("Hello World") Hello World > ``` -------------------------------- ### sortedHint Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_22_slices_sort.go.txt A hint for pdqsort when choosing the pivot. It can be unknown, increasing, or decreasing. ```APIDOC ## sortedHint ### Description A hint for pdqsort when choosing the pivot. It can be unknown, increasing, or decreasing. ### Type type sortedHint int ### Constants - **unknownHint** (sortedHint) - **increasingHint** (sortedHint) - **decreasingHint** (sortedHint) ``` -------------------------------- ### CompactFunc Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_22_slices_slices.go.txt CompactFunc is similar to Compact but uses a provided equality function to compare elements. It keeps the first element of any run of equal elements and zeroes out the rest. ```APIDOC ## CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S ### Description CompactFunc removes consecutive equal elements from a slice based on a provided equality function, keeping the first occurrence and zeroing out the rest. It modifies the slice in place and returns the new slice header. ### Parameters #### Path Parameters - **s** (S) - The input slice. - **eq** (func(E, E) bool) - An equality function that returns true if two elements are considered equal. ### Returns - **S** - The modified slice with consecutive equal elements compacted. ``` -------------------------------- ### Go sync.Once.doSlow: The slow path for Once.Do Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_21_sync.go.txt The `doSlow` method handles the less frequent execution path for `sync.Once.Do`. It uses a mutex to ensure that only one goroutine can execute the function `f` when the `Once` has not yet been completed, preventing race conditions. ```go func (o *Once) doSlow(f func()) { o.m.Lock() defer o.m.Unlock() if o.done == 0 { defer atomic.StoreUint32(&o.done, 1) f() } } ``` -------------------------------- ### Choose Pivot for Ordered Slices Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_21_slices.go.txt Chooses a pivot element within a specified range of an ordered slice. Uses different strategies based on slice length: static, median-of-three, or Tukey ninther. ```go // choosePivotOrdered chooses a pivot in data[a:b]. // // [0,8): chooses a static pivot. // [8,shortestNinther): uses the simple median-of-three method. // [shortestNinther,∞): uses the Tukey ninther method. func choosePivotOrdered[E cmp.Ordered](data []E, a, b int) (pivot int, hint sortedHint) { const ( shortestNinther = 50 maxSwaps = 4 * 3 ) l := b - a var ( swaps int i = a + l/4*1 j = a + l/4*2 k = a + l/4*3 ) if l >= 8 { if l >= shortestNinther { // Tukey ninther method, the idea came from Rust's implementation. i = medianAdjacentOrdered(data, i, &swaps) j = medianAdjacentOrdered(data, j, &swaps) k = medianAdjacentOrdered(data, k, &swaps) } // Find the median among i, j, k and stores it into j. j = medianOrdered(data, i, j, k, &swaps) } switch swaps { case 0: return j, increasingHint case maxSwaps: return j, decreasingHint default: return j, unknownHint } } ``` -------------------------------- ### Uint64 Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_22_sync_atomic_type.go.txt An atomic uint64. Supports Load, Store, Swap, and CompareAndSwap operations. ```APIDOC ## Uint64 ### Description Represents an atomic uint64. The zero value is zero. ### Methods - **Load() uint64**: Atomically loads and returns the uint64 value. - **Store(val uint64)**: Atomically stores the given uint64 value. - **Swap(new uint64) (old uint64)**: Atomically stores a new uint64 value and returns the previous value. - **CompareAndSwap(old, new uint64) (swapped bool)**: Executes a compare-and-swap operation for the uint64 value. Returns true if the swap was successful. ``` -------------------------------- ### Uint32 Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_22_sync_atomic_type.go.txt An atomic uint32. Supports Load, Store, Swap, CompareAndSwap, and Add operations. ```APIDOC ## Uint32 ### Description Represents an atomic uint32. The zero value is zero. ### Methods - **Load() uint32**: Atomically loads and returns the uint32 value. - **Store(val uint32)**: Atomically stores the given uint32 value. - **Swap(new uint32) (old uint32)**: Atomically stores a new uint32 value and returns the previous value. - **CompareAndSwap(old, new uint32) (swapped bool)**: Executes a compare-and-swap operation for the uint32 value. Returns true if the swap was successful. - **Add(delta uint32) (new uint32)**: Atomically adds delta to the current value and returns the new value. ``` -------------------------------- ### Binary search for a target in a sorted slice Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_22_slices_sort.go.txt Searches for target in a sorted slice x. Returns the index where target is found or would be inserted, and a boolean indicating if it was found. The slice must be sorted in increasing order. ```go // BinarySearch searches for target in a sorted slice and returns the position // where target is found, or the position where target would appear in the // sort order; it also returns a bool saying whether the target is really found // in the slice. The slice must be sorted in increasing order. func BinarySearch[S ~[]E, E cmp.Ordered](x S, target E) (int, bool) { // Inlining is faster than calling BinarySearchFunc with a lambda. n := len(x) // Define x[-1] < target and x[n] >= target. // Invariant: x[i-1] < target, x[j] >= target. i, j := 0, n for i < j { h := int(uint(i+j) >> 1) // avoid overflow when computing h // i ≤ h < j if cmp.Less(x[h], target) { i = h + 1 // preserves x[i-1] < target } else { j = h // preserves x[j] >= target } } // i == j, x[i-1] < target, and x[j] (= x[i]) >= target => answer is i. return i, i < n && (x[i] == target || (isNaN(x[i]) && isNaN(target))) } ``` -------------------------------- ### Embed Yaegi Interpreter Source: https://github.com/traefik/yaegi/blob/master/README.md Use this snippet to create a new interpreter instance and evaluate Go code strings. Ensure the 'interp' and 'stdlib' packages are imported. ```go package main import ( "github.com/traefik/yaegi/interp" "github.com/traefik/yaegi/stdlib" ) func main() { i := interp.New(interp.Options{}) i.Use(stdlib.Symbols) _, err := i.Eval(`import "fmt"`) if err != nil { panic(err) } _, err = i.Eval(`fmt.Println("Hello Yaegi")`) if err != nil { panic(err) } } ``` -------------------------------- ### Break Patterns for Ordered Slices Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_21_slices.go.txt Scatters elements in an ordered slice to break patterns that might cause imbalanced partitions in quicksort. Requires a minimum length of 8. ```go // breakPatternsOrdered scatters some elements around in an attempt to break some patterns // that might cause imbalanced partitions in quicksort. func breakPatternsOrdered[E cmp.Ordered](data []E, a, b int) { length := b - a if length >= 8 { random := xorshift(length) modulus := nextPowerOfTwo(length) for idx := a + (length/4)*2 - 1; idx <= a+(length/4)*2+1; idx++ { other := int(uint(random.Next()) & (modulus - 1)) if other >= length { other -= length } data[idx], data[a+other] = data[a+other], data[idx] } } } ``` -------------------------------- ### Grow: Increase Slice Capacity Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_22_slices_slices.go.txt Use Grow to ensure a slice has enough capacity to hold at least n additional elements. This prevents reallocations when appending. Panics if n is negative or too large to allocate. ```go func Grow[S ~[]E, E any](s S, n int) S { if n < 0 { panic("cannot be negative") } if n -= cap(s) - len(s); n > 0 { s = append(s[:cap(s)], make([]E, n)...)[:len(s)] } return s } ``` -------------------------------- ### Compact Slice Elements with Custom Equality Function in Go Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_21_slices.go.txt Similar to Compact, but uses a provided equality function to determine if elements are duplicates. Keeps the first element of any run of equal elements. ```go func CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S { if len(s) < 2 { return s } i := 1 for k := 1; k < len(s); k++ { if !eq(s[k], s[k-1]) { if i != k { s[i] = s[k] } i++ } } return s[:i] } ``` -------------------------------- ### Int64 Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_22_sync_atomic_type.go.txt An atomic int64. Supports Load, Store, Swap, CompareAndSwap, and Add operations. ```APIDOC ## Int64 ### Description Represents an atomic int64. The zero value is zero. ### Methods - **Load() int64**: Atomically loads and returns the int64 value. - **Store(val int64)**: Atomically stores the given int64 value. - **Swap(new int64) (old int64)**: Atomically stores a new int64 value and returns the previous value. - **CompareAndSwap(old, new int64) (swapped bool)**: Executes a compare-and-swap operation for the int64 value. Returns true if the swap was successful. - **Add(delta int64) (new int64)**: Atomically adds delta to the current value and returns the new value. ``` -------------------------------- ### Int32 Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_22_sync_atomic_type.go.txt An atomic int32. Supports Load, Store, Swap, CompareAndSwap, and Add operations. ```APIDOC ## Int32 ### Description Represents an atomic int32. The zero value is zero. ### Methods - **Load() int32**: Atomically loads and returns the int32 value. - **Store(val int32)**: Atomically stores the given int32 value. - **Swap(new int32) (old int32)**: Atomically stores a new int32 value and returns the previous value. - **CompareAndSwap(old, new int32) (swapped bool)**: Executes a compare-and-swap operation for the int32 value. Returns true if the swap was successful. - **Add(delta int32) (new int32)**: Atomically adds delta to the current value and returns the new value. ``` -------------------------------- ### Go Scripting with Shebang Line Source: https://github.com/traefik/yaegi/blob/master/README.md Use Yaegi for Go scripting by adding a shebang line to your script file. This allows direct execution of Go code as a script. ```shell cat /tmp/test #!/usr/bin/env yaegi package main import "fmt" func main() { fmt.Println("test") } $ ls -la /tmp/test -rwxr-xr-x 1 dow184 dow184 93 Jan 6 13:38 /tmp/test $ /tmp/test test ``` -------------------------------- ### Find the minimum value using a custom comparison function Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_22_slices_sort.go.txt Returns the minimal value in slice x using a custom comparison function. Panics if x is empty. If multiple minimal elements exist, the first one encountered is returned. ```go // MinFunc returns the minimal value in x, using cmp to compare elements. // It panics if x is empty. If there is more than one minimal element // according to the cmp function, MinFunc returns the first one. func MinFunc[S ~[]E, E any](x S, cmp func(a, b E) int) E { if len(x) < 1 { panic("slices.MinFunc: empty list") } m := x[0] for i := 1; i < len(x); i++ { if cmp(x[i], m) < 0 { m = x[i] } } return m } ``` -------------------------------- ### Concat: Concatenate Multiple Slices Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_22_slices_slices.go.txt Creates and returns a new slice that is the concatenation of all provided slices. It first calculates the total size, grows a new slice to that capacity, and then appends each input slice. ```go func Concat[S ~[]E, E any](slices ...S) S { size := 0 for _, s := range slices { size += len(s) if size < 0 { panic("len out of range") } } newslice := Grow[S](nil, size) for _, s := range slices { newslice = append(newslice, s...) } return newslice } ``` -------------------------------- ### partitionOrdered Function Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_21_slices.go.txt Performs a single quicksort partition step. It rearranges elements such that elements less than the pivot are to its left, and elements greater than or equal to the pivot are to its right. Returns the new pivot index and a boolean indicating if the slice was already partitioned. ```go func partitionOrdered[E cmp.Ordered](data []E, a, b, pivot int) (newpivot int, alreadyPartitioned bool) { data[a], data[pivot] = data[pivot], data[a] i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned for i <= j && cmp.Less(data[i], data[a]) { i++ } for i <= j && !cmp.Less(data[j], data[a]) { j-- } if i > j { data[j], data[a] = data[a], data[j] return j, true } data[i], data[j] = data[j], data[i] i++ j-- for { for i <= j && cmp.Less(data[i], data[a]) { i++ } for i <= j && !cmp.Less(data[j], data[a]) { j-- } if i > j { break } data[i], data[j] = data[j], data[i] i++ j-- } data[j], data[a] = data[a], data[j] return j, false } ``` -------------------------------- ### EqualFunc Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_22_maps_maps.go.txt Is like Equal, but compares values using a provided equality function. Keys are still compared using the equality operator (==). ```APIDOC ## EqualFunc ### Description Is like Equal, but compares values using a provided equality function (`eq`). Keys are still compared using the equality operator (==). ### Signature `func EqualFunc[M1 ~map[K]V1, M2 ~map[K]V2, K comparable, V1, V2 any](m1 M1, m2 M2, eq func(V1, V2) bool) bool` ### Parameters - `m1` (M1): The first map. - `m2` (M2): The second map. - `eq` (func(V1, V2) bool): A function that compares two values for equality. ### Returns - `bool`: `true` if the maps are equal according to the provided function, `false` otherwise. ``` -------------------------------- ### Compare Maps with Custom Equality - Go Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_21_maps.go.txt Use EqualFunc for maps where values need custom comparison logic. The 'eq' function determines value equality, while keys are compared using '=='. ```go func EqualFunc[M1 ~map[K]V1, M2 ~map[K]V2, K comparable, V1, V2 any](m1 M1, m2 M2, eq func(V1, V2) bool) bool { if len(m1) != len(m2) { return false } for k, v1 := range m1 { if v2, ok := m2[k]; !ok || !eq(v1, v2) { return false } } return true } ``` -------------------------------- ### Go: Compare Slices with Custom Function Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_22_slices_slices.go.txt Compares elements of two slices sequentially using a custom comparison function. Returns the first non-zero result, or based on length if one slice is a prefix of the other. ```go func CompareFunc[S1 ~[]E1, S2 ~[]E2, E1, E2 any](s1 S1, s2 S2, cmp func(E1, E2) int) int { for i, v1 := range s1 { if i >= len(s2) { return +1 } v2 := s2[i] if c := cmp(v1, v2); c != 0 { return c } } if len(s1) < len(s2) { return -1 } return 0 } ``` -------------------------------- ### Clone a Slice in Go Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_21_slices.go.txt Creates a shallow copy of a slice. Preserves the nil value if the input slice is nil. ```go func Clone[S ~[]E, E any](s S) S { // Preserve nil in case it matters. if s == nil { return nil } return append(S([]E{}), s...) } ``` -------------------------------- ### Compare Maps for Equality - Go Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_21_maps.go.txt Use Equal to check if two maps have the same key-value pairs. Values are compared using the '==' operator. Ensure maps have the same length before comparison. ```go func Equal[M1, M2 ~map[K]V, K, V comparable](m1 M1, m2 M2) bool { if len(m1) != len(m2) { return false } for k, v1 := range m1 { if v2, ok := m2[k]; !ok || v1 != v2 { return false } } return true } ``` -------------------------------- ### Go: Compare Slices Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_22_slices_slices.go.txt Compares elements of two slices sequentially using cmp.Compare. Returns the result of the first non-matching pair, or based on length if one slice is a prefix of the other. ```go func Compare[S ~[]E, E cmp.Ordered](s1, s2 S) int { for i, v1 := range s1 { if i >= len(s2) { return +1 } v2 := s2[i] if c := cmp.Compare(v1, v2); c != 0 { return c } } if len(s1) < len(s2) { return -1 } return 0 } ``` -------------------------------- ### Clone Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_22_maps_maps.go.txt Returns a shallow copy of the map. The new keys and values are set using ordinary assignment. ```APIDOC ## Clone ### Description Returns a shallow copy of the map `m`. This is a shallow clone: the new keys and values are set using ordinary assignment. ### Signature `func Clone[M ~map[K]V, K comparable, V any](m M) M` ### Parameters - `m` (M): The map to clone. ### Returns - `M`: A shallow copy of the input map. ``` -------------------------------- ### Min Source: https://github.com/traefik/yaegi/blob/master/stdlib/generic/go1_22_slices_sort.go.txt Returns the minimal value in x. It panics if x is empty. For floating-point E, Min propagates NaNs (any NaN value in x forces the output to be NaN). ```APIDOC ## Min[S ~[]E, E cmp.Ordered](x S) E ### Description Returns the minimal value in x. It panics if x is empty. For floating-point E, Min propagates NaNs (any NaN value in x forces the output to be NaN). ### Parameters * `x` (S) - The slice to find the minimum value from. ### Returns * `E` - The minimal value in the slice. ```