mirror of
https://github.com/gonum/gonum.git
synced 2025-10-09 17:10:16 +08:00
Combined Find and FindFirst into one function
This commit is contained in:
63
floats.go
63
floats.go
@@ -5,7 +5,7 @@ import "math"
|
|||||||
// InsufficientElements is an error type used by FindFirst
|
// InsufficientElements is an error type used by FindFirst
|
||||||
type InsufficientElements struct{}
|
type InsufficientElements struct{}
|
||||||
|
|
||||||
func (i InsufficientElements) Error() string {
|
func (i *InsufficientElements) Error() string {
|
||||||
return "Insufficient elements found"
|
return "Insufficient elements found"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,38 +109,51 @@ func EqLen(slices ...[]float64) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find applies a function returning a boolean to the elements of the slice
|
// Find finds the first k indices of the slice s for which
|
||||||
// and returns a list of indices for which the value is true
|
// the function f returns true and stores them in the slice
|
||||||
func Find(s []float64, f func(float64) bool) (inds []int) {
|
// inds. If k < 0, all such elements are found.
|
||||||
// Not sure what an appropriate capacity is here. Don't want to make
|
// Find will reslice inds to have 0 length, and will append
|
||||||
// it the length of the slice because if the slice is large that is
|
// found indices to inds.
|
||||||
// a lot of potentially wasted memory
|
// If there are fewer than k elements in s satisfying f,
|
||||||
inds = make([]int, 0)
|
// all of the found elements will be returned along with an
|
||||||
for i, val := range s {
|
// InsufficientElements error
|
||||||
if f(val) {
|
// TODO: Add example for nil slice, appending to the end of a larger slice
|
||||||
inds = append(inds, i)
|
// reusing memory, insufficient elements
|
||||||
}
|
func Find(inds []int, k int, s []float64, f func(float64) bool) ([]int, error) {
|
||||||
}
|
|
||||||
return inds
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindFirst applies a function returning a boolean to the elements of the slice
|
// inds is also returned to allow for calling with nil
|
||||||
// and returns a list of the first k indices for which the value is true.
|
|
||||||
// If there are fewer than k indices for which the value is true, it returns
|
// Reslice inds to have zero length
|
||||||
// the found indices and an error.
|
inds = inds[:0]
|
||||||
func FindFirst(s []float64, f func(float64) bool, k int) (inds []int, err error) {
|
|
||||||
count := 0
|
// If zero elements requested, can just return
|
||||||
inds = make([]int, 0, k)
|
if k == 0 {
|
||||||
|
return inds, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If k < 0, return all of the found indices
|
||||||
|
if k < 0 {
|
||||||
for i, val := range s {
|
for i, val := range s {
|
||||||
if f(val) {
|
if f(val) {
|
||||||
inds = append(inds, i)
|
inds = append(inds, i)
|
||||||
count++
|
}
|
||||||
if count == k {
|
}
|
||||||
|
return inds, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, find the first k elements
|
||||||
|
nFound := 0
|
||||||
|
for i, val := range s {
|
||||||
|
if f(val) {
|
||||||
|
inds = append(inds, i)
|
||||||
|
nFound++
|
||||||
|
if nFound == k {
|
||||||
return inds, nil
|
return inds, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return inds, InsufficientElements{}
|
// Finished iterating over the loop, which means k elements were not found
|
||||||
|
return inds, &InsufficientElements{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogSpan returns a set of N equally spaced points in log space between l and u, where N
|
// LogSpan returns a set of N equally spaced points in log space between l and u, where N
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
package sliceops
|
package sliceops
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"math"
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -111,50 +111,63 @@ func TestEqLen(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func eqIntSlice(one, two []int) string {
|
||||||
|
if len(one) != len(two) {
|
||||||
|
return "Length mismatch"
|
||||||
|
}
|
||||||
|
for i, val := range one {
|
||||||
|
if val != two[i] {
|
||||||
|
return "Index " + strconv.Itoa(i) + " mismatch"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func TestFind(t *testing.T) {
|
func TestFind(t *testing.T) {
|
||||||
s := []float64{3, 4, 1, 7, 5}
|
s := []float64{3, 4, 1, 7, 5}
|
||||||
f := func(v float64) bool { return v > 3.5 }
|
f := func(v float64) bool { return v > 3.5 }
|
||||||
trueInds := []int{1, 3, 4}
|
allTrueInds := []int{1, 3, 4}
|
||||||
inds := Find(s, f)
|
|
||||||
if len(inds) != len(trueInds) {
|
|
||||||
t.Errorf("Wrong number of elements returned")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for i, val := range trueInds {
|
|
||||||
if inds[i] != val {
|
|
||||||
t.Errorf("Index mismatch")
|
|
||||||
fmt.Println(trueInds)
|
|
||||||
fmt.Println(inds)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFindFirst(t *testing.T) {
|
// Test finding first two elements
|
||||||
s := []float64{3, 4, 1, 7, 5}
|
inds, err := Find(nil, 2, s, f)
|
||||||
f := func(v float64) bool { return v > 3.5 }
|
|
||||||
trueInds := []int{1, 3}
|
|
||||||
k := 2
|
|
||||||
inds, err := FindFirst(s, f, k)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Incorrectly did not find enough elements")
|
t.Errorf("Find first two: Improper error return")
|
||||||
}
|
}
|
||||||
if len(inds) != len(trueInds) {
|
trueInds := allTrueInds[:2]
|
||||||
t.Errorf("Wrong number of elements returned")
|
str := eqIntSlice(inds, trueInds)
|
||||||
return
|
if str != "" {
|
||||||
|
t.Errorf("Find first two: " + str)
|
||||||
}
|
}
|
||||||
for i, val := range trueInds {
|
|
||||||
if inds[i] != val {
|
// Test finding first two elements with non nil slice
|
||||||
t.Errorf("Index mismatch")
|
inds = []int{1, 2, 3, 4, 5, 6}
|
||||||
fmt.Println(trueInds)
|
inds, err = Find(inds, 2, s, f)
|
||||||
fmt.Println(inds)
|
if err != nil {
|
||||||
return
|
t.Errorf("Find first two non-nil: Improper error return")
|
||||||
}
|
}
|
||||||
|
str = eqIntSlice(inds, trueInds)
|
||||||
|
if str != "" {
|
||||||
|
t.Errorf("Find first two non-nil: " + str)
|
||||||
}
|
}
|
||||||
f = func(v float64) bool { return v > 6.0 }
|
|
||||||
inds, err = FindFirst(s, f, k)
|
// Test finding too many elements
|
||||||
|
inds, err = Find(inds, 4, s, f)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("Incorrectly found enough elements")
|
t.Errorf("Request too many: No error returned")
|
||||||
|
}
|
||||||
|
str = eqIntSlice(inds, allTrueInds)
|
||||||
|
if str != "" {
|
||||||
|
t.Errorf("Request too many: Does not match all of the inds: " + str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test finding all elements
|
||||||
|
inds, err = Find(nil, -1, s, f)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Find all: Improper error returned")
|
||||||
|
}
|
||||||
|
str = eqIntSlice(inds, allTrueInds)
|
||||||
|
if str != "" {
|
||||||
|
t.Errorf("Find all: Does not match all of the inds: " + str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user