diff --git a/core/slice/slice.odin b/core/slice/slice.odin index 99ad15547..c917e6d9e 100644 --- a/core/slice/slice.odin +++ b/core/slice/slice.odin @@ -96,9 +96,37 @@ contains :: proc(array: $T/[]$E, value: E) -> bool where intrinsics.type_is_comp return found } +/* +Searches the given slice for the given element in O(n) time. + +If you need a custom search condition, see `linear_search_proc` + +Inputs: +- array: The slice to search in. +- key: The element to search for. + +Returns: +- index: The index `i`, such that `array[i]` is the first occurrence of `key` in `array`, or -1 if `key` is not present in `array`. + +Example: + index: int + found: bool + + a := []i32{10, 10, 10, 20} + + index, found = linear_search_reverse(a, 10) + assert(index == 0 && found == true) + + index, found = linear_search_reverse(a, 30) + assert(index == -1 && found == false) + + // Note that `index == 1`, since it is relative to `a[2:]` + index, found = linear_search_reverse(a[2:], 20) + assert(index == 1 && found == true) +*/ @(require_results) linear_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool) - where intrinsics.type_is_comparable(T) #no_bounds_check { + where intrinsics.type_is_comparable(T) { for x, i in array { if x == key { return i, true @@ -107,8 +135,18 @@ linear_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool) return -1, false } +/* +Searches the given slice for the first element satisfying predicate `f` in O(n) time. + +Inputs: +- array: The slice to search in. +- f: The search condition. + +Returns: +- index: The index `i`, such that `array[i]` is the first `x` in `array` for which `f(x) == true`, or -1 if such `x` does not exist. +*/ @(require_results) -linear_search_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, found: bool) #no_bounds_check { +linear_search_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, found: bool) { for x, i in array { if f(x) { return i, true @@ -118,22 +156,88 @@ linear_search_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, f } /* - Binary search searches the given slice for the given element. - If the slice is not sorted, the returned index is unspecified and meaningless. +Searches the given slice for the given element in O(n) time, starting from the +slice end. - If the value is found then the returned int is the index of the matching element. - If there are multiple matches, then any one of the matches could be returned. +If you need a custom search condition, see `linear_search_reverse_proc` - If the value is not found then the returned int is the index where a matching - element could be inserted while maintaining sorted order. +Inputs: +- array: The slice to search in. +- key: The element to search for. - # Examples +Returns: +- index: The index `i`, such that `array[i]` is the last occurrence of `key` in `array`, or -1 if `key` is not present in `array`. +Example: + index: int + found: bool + + a := []i32{10, 10, 10, 20} + + index, found = linear_search_reverse(a, 20) + assert(index == 3 && found == true) + + index, found = linear_search_reverse(a, 10) + assert(index == 2 && found == true) + + index, found = linear_search_reverse(a, 30) + assert(index == -1 && found == false) + + // Note that `index == 1`, since it is relative to `a[2:]` + index, found = linear_search_reverse(a[2:], 20) + assert(index == 1 && found == true) +*/ +@(require_results) +linear_search_reverse :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool) + where intrinsics.type_is_comparable(T) { + #reverse for x, i in array { + if x == key { + return i, true + } + } + return -1, false +} + +/* +Searches the given slice for the last element satisfying predicate `f` in O(n) +time, starting from the slice end. + +Inputs: +- array: The slice to search in. +- f: The search condition. + +Returns: +- index: The index `i`, such that `array[i]` is the last `x` in `array` for which `f(x) == true`, or -1 if such `x` does not exist. +*/ +@(require_results) +linear_search_reverse_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, found: bool) { + #reverse for x, i in array { + if f(x) { + return i, true + } + } + return -1, false +} + +/* +Searches the given slice for the given element. +If the slice is not sorted, the returned index is unspecified and meaningless. + +If the value is found then the returned int is the index of the matching element. +If there are multiple matches, then any one of the matches could be returned. + +If the value is not found then the returned int is the index where a matching +element could be inserted while maintaining sorted order. + +For slices of more complex types see: `binary_search_by` + +Example: + /* Looks up a series of four elements. The first is found, with a uniquely determined position; the second and third are not found; the fourth could match any position in `[1, 4]`. + */ - ``` index: int found: bool @@ -150,9 +254,6 @@ linear_search_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, f index, found = slice.binary_search(s, 1) assert(index >= 1 && index <= 4 && found == true) - ``` - - For slices of more complex types see: binary_search_by */ @(require_results) binary_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool) @@ -161,21 +262,21 @@ binary_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool) } /* - Binary search searches the given slice for the given element. - If the slice is not sorted, the returned index is unspecified and meaningless. +Searches the given slice for the given element. +If the slice is not sorted, the returned index is unspecified and meaningless. - If the value is found then the returned int is the index of the matching element. - If there are multiple matches, then any one of the matches could be returned. +If the value is found then the returned int is the index of the matching element. +If there are multiple matches, then any one of the matches could be returned. - If the value is not found then the returned int is the index where a matching - element could be inserted while maintaining sorted order. +If the value is not found then the returned int is the index where a matching +element could be inserted while maintaining sorted order. - The array elements and key may be different types. This allows the filter procedure - to compare keys against a slice of structs, one struct value at a time. +The array elements and key may be different types. This allows the filter procedure +to compare keys against a slice of structs, one struct value at a time. - Returns: - index: int - found: bool +Returns: +- index: int +- found: bool */ @(require_results) diff --git a/tests/core/slice/test_core_slice.odin b/tests/core/slice/test_core_slice.odin index 16d5b23a4..9c77f872d 100644 --- a/tests/core/slice/test_core_slice.odin +++ b/tests/core/slice/test_core_slice.odin @@ -306,3 +306,38 @@ test_compare_empty :: proc(t: ^testing.T) { testing.expectf(t, slice.equal(c[:], d[:]), "Expected two separate empty slices of two dynamic arrays to be equal") } + +@test +test_linear_search_reverse :: proc(t: ^testing.T) { + index: int + found: bool + + s := []i32{0, 50, 50, 100} + + index, found = slice.linear_search_reverse(s, 100) + testing.expect(t, found) + testing.expect_value(t, index, len(s) - 1) + + index, found = slice.linear_search_reverse(s[len(s) - 1:], 100) + testing.expect(t, found) + testing.expect_value(t, index, 0) + + index, found = slice.linear_search_reverse(s, 50) + testing.expect(t, found) + testing.expect_value(t, index, 2) + + index, found = slice.linear_search_reverse(s, 0) + testing.expect(t, found) + testing.expect_value(t, index, 0) + + index, found = slice.linear_search_reverse(s, -1) + testing.expect(t, !found) + + less_than_80 :: proc(x: i32) -> bool { + return x < 80 + } + + index, found = slice.linear_search_reverse_proc(s, less_than_80) + testing.expect(t, found) + testing.expect_value(t, index, 2) +}