mirror of
https://github.com/golang/go
synced 2024-11-22 00:14:42 -07:00
sort: invert meaning of f in Search
Backwards incompatible change, but makes it easier to reason about non-idiomatic searches: now f specifies what is sought. R=gri CC=golang-dev https://golang.org/cl/3195042
This commit is contained in:
parent
19f0e4603d
commit
285298b975
@ -94,7 +94,7 @@ func (p Pos) IsValid() bool {
|
|||||||
|
|
||||||
|
|
||||||
func searchFiles(a []*File, x int) int {
|
func searchFiles(a []*File, x int) int {
|
||||||
return sort.Search(len(a), func(i int) bool { return a[i].base <= x }) - 1
|
return sort.Search(len(a), func(i int) bool { return a[i].base > x }) - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -252,12 +252,12 @@ func (f *File) Position(offset int) Position {
|
|||||||
|
|
||||||
|
|
||||||
func searchUints(a []int, x int) int {
|
func searchUints(a []int, x int) int {
|
||||||
return sort.Search(len(a), func(i int) bool { return a[i] <= x }) - 1
|
return sort.Search(len(a), func(i int) bool { return a[i] > x }) - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func searchLineInfos(a []lineInfo, x int) int {
|
func searchLineInfos(a []lineInfo, x int) int {
|
||||||
return sort.Search(len(a), func(i int) bool { return a[i].offset <= x }) - 1
|
return sort.Search(len(a), func(i int) bool { return a[i].offset > x }) - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,11 +7,11 @@
|
|||||||
package sort
|
package sort
|
||||||
|
|
||||||
// Search uses binary search to find and return the smallest index i
|
// Search uses binary search to find and return the smallest index i
|
||||||
// in [0, n) at which f(i) is false, assuming that on the range [0, n),
|
// in [0, n) at which f(i) is true, assuming that on the range [0, n),
|
||||||
// f(i) == false implies f(i+1) == false. That is, Search requires that
|
// f(i) == true implies f(i+1) == true. That is, Search requires that
|
||||||
// f is true for some (possibly empty) prefix of the input range [0, n)
|
// f is false for some (possibly empty) prefix of the input range [0, n)
|
||||||
// and then false for the (possibly empty) remainder; Search returns
|
// and then true for the (possibly empty) remainder; Search returns
|
||||||
// the first false index. If there is no such index, Search returns n.
|
// the first true index. If there is no such index, Search returns n.
|
||||||
// Search calls f(i) only for i in the range [0, n).
|
// Search calls f(i) only for i in the range [0, n).
|
||||||
//
|
//
|
||||||
// A common use of Search is to find the index i for a value x in
|
// A common use of Search is to find the index i for a value x in
|
||||||
@ -21,19 +21,19 @@ package sort
|
|||||||
// ordered.
|
// ordered.
|
||||||
//
|
//
|
||||||
// For instance, given a slice data sorted in ascending order,
|
// For instance, given a slice data sorted in ascending order,
|
||||||
// the call Search(len(data), func(i int) bool { return data[i] < 23 })
|
// the call Search(len(data), func(i int) bool { return data[i] >= 23 })
|
||||||
// returns the smallest index i such that data[i] >= 23. If the caller
|
// returns the smallest index i such that data[i] >= 23. If the caller
|
||||||
// wants to find whether 23 is in the slice, it must test data[i] == 23
|
// wants to find whether 23 is in the slice, it must test data[i] == 23
|
||||||
// separately.
|
// separately.
|
||||||
//
|
//
|
||||||
// Searching data sorted in descending order would use the >
|
// Searching data sorted in descending order would use the <=
|
||||||
// operator instead of the < operator.
|
// operator instead of the >= operator.
|
||||||
//
|
//
|
||||||
// To complete the example above, the following code tries to find the value
|
// To complete the example above, the following code tries to find the value
|
||||||
// x in an integer slice data sorted in ascending order:
|
// x in an integer slice data sorted in ascending order:
|
||||||
//
|
//
|
||||||
// x := 23
|
// x := 23
|
||||||
// i := sort.Search(len(data), func(i int) bool { return data[i] < x })
|
// i := sort.Search(len(data), func(i int) bool { return data[i] >= x })
|
||||||
// if i < len(data) && data[i] == x {
|
// if i < len(data) && data[i] == x {
|
||||||
// // x is present at data[i]
|
// // x is present at data[i]
|
||||||
// } else {
|
// } else {
|
||||||
@ -47,7 +47,7 @@ package sort
|
|||||||
// var s string
|
// var s string
|
||||||
// fmt.Printf("Pick an integer from 0 to 100.\n")
|
// fmt.Printf("Pick an integer from 0 to 100.\n")
|
||||||
// answer := sort.Search(100, func(i int) bool {
|
// answer := sort.Search(100, func(i int) bool {
|
||||||
// fmt.Printf("Is your number > %d? ", i)
|
// fmt.Printf("Is your number <= %d? ", i)
|
||||||
// fmt.Scanf("%s", &s)
|
// fmt.Scanf("%s", &s)
|
||||||
// return s != "" && s[0] == 'y'
|
// return s != "" && s[0] == 'y'
|
||||||
// })
|
// })
|
||||||
@ -55,19 +55,19 @@ package sort
|
|||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
func Search(n int, f func(int) bool) int {
|
func Search(n int, f func(int) bool) int {
|
||||||
// Define f(-1) == true and f(n) == false.
|
// Define f(-1) == false and f(n) == true.
|
||||||
// Invariant: f(i-1) == true, f(j) == false.
|
// Invariant: f(i-1) == false, f(j) == true.
|
||||||
i, j := 0, n
|
i, j := 0, n
|
||||||
for i < j {
|
for i < j {
|
||||||
h := i + (j-i)/2 // avoid overflow when computing h
|
h := i + (j-i)/2 // avoid overflow when computing h
|
||||||
// i ≤ h < j
|
// i ≤ h < j
|
||||||
if f(h) {
|
if !f(h) {
|
||||||
i = h + 1 // preserves f(i-1) == true
|
i = h + 1 // preserves f(i-1) == false
|
||||||
} else {
|
} else {
|
||||||
j = h // preserves f(j) == false
|
j = h // preserves f(j) == true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// i == j, f(i-1) == true, and f(j) (= f(i)) == false => answer is i.
|
// i == j, f(i-1) == false, and f(j) (= f(i)) == true => answer is i.
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ func Search(n int, f func(int) bool) int {
|
|||||||
// as specified by Search. The array must be sorted in ascending order.
|
// as specified by Search. The array must be sorted in ascending order.
|
||||||
//
|
//
|
||||||
func SearchInts(a []int, x int) int {
|
func SearchInts(a []int, x int) int {
|
||||||
return Search(len(a), func(i int) bool { return a[i] < x })
|
return Search(len(a), func(i int) bool { return a[i] >= x })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ func SearchInts(a []int, x int) int {
|
|||||||
// as specified by Search. The array must be sorted in ascending order.
|
// as specified by Search. The array must be sorted in ascending order.
|
||||||
//
|
//
|
||||||
func SearchFloats(a []float, x float) int {
|
func SearchFloats(a []float, x float) int {
|
||||||
return Search(len(a), func(i int) bool { return a[i] < x })
|
return Search(len(a), func(i int) bool { return a[i] >= x })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -94,7 +94,7 @@ func SearchFloats(a []float, x float) int {
|
|||||||
// as specified by Search. The array must be sorted in ascending order.
|
// as specified by Search. The array must be sorted in ascending order.
|
||||||
//
|
//
|
||||||
func SearchStrings(a []string, x string) int {
|
func SearchStrings(a []string, x string) int {
|
||||||
return Search(len(a), func(i int) bool { return a[i] < x })
|
return Search(len(a), func(i int) bool { return a[i] >= x })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import "testing"
|
|||||||
|
|
||||||
func f(a []int, x int) func(int) bool {
|
func f(a []int, x int) func(int) bool {
|
||||||
return func(i int) bool {
|
return func(i int) bool {
|
||||||
return a[i] < x
|
return a[i] >= x
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,12 +23,12 @@ var tests = []struct {
|
|||||||
i int
|
i int
|
||||||
}{
|
}{
|
||||||
{"empty", 0, nil, 0},
|
{"empty", 0, nil, 0},
|
||||||
{"1 1", 1, func(i int) bool { return i < 1 }, 1},
|
{"1 1", 1, func(i int) bool { return i >= 1 }, 1},
|
||||||
{"1 false", 1, func(i int) bool { return false }, 0},
|
{"1 true", 1, func(i int) bool { return true }, 0},
|
||||||
{"1 true", 1, func(i int) bool { return true }, 1},
|
{"1 false", 1, func(i int) bool { return false }, 1},
|
||||||
{"1e9 991", 1e9, func(i int) bool { return i < 991 }, 991},
|
{"1e9 991", 1e9, func(i int) bool { return i >= 991 }, 991},
|
||||||
{"1e9 false", 1e9, func(i int) bool { return false }, 0},
|
{"1e9 true", 1e9, func(i int) bool { return true }, 0},
|
||||||
{"1e9 true", 1e9, func(i int) bool { return true }, 1e9},
|
{"1e9 false", 1e9, func(i int) bool { return false }, 1e9},
|
||||||
{"data -20", len(data), f(data, -20), 0},
|
{"data -20", len(data), f(data, -20), 0},
|
||||||
{"data -10", len(data), f(data, -10), 0},
|
{"data -10", len(data), f(data, -10), 0},
|
||||||
{"data -9", len(data), f(data, -9), 1},
|
{"data -9", len(data), f(data, -9), 1},
|
||||||
@ -41,8 +41,9 @@ var tests = []struct {
|
|||||||
{"data 101", len(data), f(data, 101), 12},
|
{"data 101", len(data), f(data, 101), 12},
|
||||||
{"data 10000", len(data), f(data, 10000), 13},
|
{"data 10000", len(data), f(data, 10000), 13},
|
||||||
{"data 10001", len(data), f(data, 10001), 14},
|
{"data 10001", len(data), f(data, 10001), 14},
|
||||||
{"descending a", 7, func(i int) bool { return []int{99, 99, 59, 42, 7, 0, -1, -1}[i] > 7 }, 4},
|
{"descending a", 7, func(i int) bool { return []int{99, 99, 59, 42, 7, 0, -1, -1}[i] <= 7 }, 4},
|
||||||
{"descending 7", 1e9, func(i int) bool { return 1e9-i > 7 }, 1e9 - 7},
|
{"descending 7", 1e9, func(i int) bool { return 1e9-i <= 7 }, 1e9 - 7},
|
||||||
|
{"overflow", 2e9, func(i int) bool { return false }, 2e9},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -79,7 +80,7 @@ func TestSearchEfficiency(t *testing.T) {
|
|||||||
max := log2(n)
|
max := log2(n)
|
||||||
for x := 0; x < n; x += step {
|
for x := 0; x < n; x += step {
|
||||||
count := 0
|
count := 0
|
||||||
i := Search(n, func(i int) bool { count++; return i < x })
|
i := Search(n, func(i int) bool { count++; return i >= x })
|
||||||
if i != x {
|
if i != x {
|
||||||
t.Errorf("n = %d: expected index %d; got %d", n, x, i)
|
t.Errorf("n = %d: expected index %d; got %d", n, x, i)
|
||||||
}
|
}
|
||||||
@ -119,3 +120,18 @@ func TestSearchWrappers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Abstract exhaustive test: all sizes up to 100,
|
||||||
|
// all possible return values. If there are any small
|
||||||
|
// corner cases, this test exercises them.
|
||||||
|
func TestSearchExhaustive(t *testing.T) {
|
||||||
|
for size := 0; size <= 100; size++ {
|
||||||
|
for targ := 0; targ <= size; targ++ {
|
||||||
|
i := Search(size, func(i int) bool { return i >= targ })
|
||||||
|
if i != targ {
|
||||||
|
t.Errorf("Search(%d, %d) = %d", size, targ, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user