From cff8a71275241b0a4caae658ce91999301d1a42e Mon Sep 17 00:00:00 2001 From: Ed Schouten Date: Tue, 27 Feb 2024 12:45:07 +0100 Subject: [PATCH] container/heap: use sort.Interface where possible Functions heap.Pop(), heap.Push() and heap.Remove() use type heap.Interface. This type is equal to sort.Interface, with Push() and Pop() methods added. This is necessary, because these functions affect the size of the resulting heap. Functions heap.Fix() and heap.Init() are different. They may compare and swap elements in the underlying container, but never alter its size. It is therefore possible to just use sort.Interface. This would make these functions a lot more pleasant to use in situations where you know the number of elements in the heap is fixed. Consider the case where you use these functions to merge the results of n infinitely long monotonically increasing sequences. For example, logs received from multiple sources that need to be turned into a single stream of events sorted by time. This can now be implemented as follows: type Value ... type Sequence <-chan Value type PeekedSequence struct { First Value Remainder Sequence } type SequenceHeap []PeekedSequence func (h SequenceHeap) Len() int { return len(h) } func (h SequenceHeap) Less(i, j int) bool { return h[i].First < h[j].First } func (h SequenceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } func MergeSequences(sequences []Sequence, out chan<- Value) { h := make(SequenceHeap, 0, len(sequences)) for _, s := range sequences { h = append(h, PeekedSequence{ First: <-s, Remainder: s, }) } heap.Init(h) for { out <- h[0].First h[0].First = <-h[0].Remainder heap.Fix(h, 0) } } Before this change was made, SequenceHeap had to provide Pop() and Push() methods that are known to be unused. --- src/container/heap/heap.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/container/heap/heap.go b/src/container/heap/heap.go index 3ad218e69333c4..56a268c9e5b19b 100644 --- a/src/container/heap/heap.go +++ b/src/container/heap/heap.go @@ -38,7 +38,7 @@ type Interface interface { // Init is idempotent with respect to the heap invariants // and may be called whenever the heap invariants may have been invalidated. // The complexity is O(n) where n = h.Len(). -func Init(h Interface) { +func Init(h sort.Interface) { // heapify n := h.Len() for i := n/2 - 1; i >= 0; i-- { @@ -80,13 +80,13 @@ func Remove(h Interface, i int) any { // Changing the value of the element at index i and then calling Fix is equivalent to, // but less expensive than, calling [Remove](h, i) followed by a Push of the new value. // The complexity is O(log n) where n = h.Len(). -func Fix(h Interface, i int) { +func Fix(h sort.Interface, i int) { if !down(h, i, h.Len()) { up(h, i) } } -func up(h Interface, j int) { +func up(h sort.Interface, j int) { for { i := (j - 1) / 2 // parent if i == j || !h.Less(j, i) { @@ -97,7 +97,7 @@ func up(h Interface, j int) { } } -func down(h Interface, i0, n int) bool { +func down(h sort.Interface, i0, n int) bool { i := i0 for { j1 := 2*i + 1