ソースを参照

Merge pull request #4304 from kawaii-Code/linear_search_reverse

Add linear_search_reverse and linear_search_reverse_proc
Laytan 10 ヶ月 前
コミット
38fedf2631
2 ファイル変更160 行追加24 行削除
  1. 125 24
      core/slice/slice.odin
  2. 35 0
      tests/core/slice/test_core_slice.odin

+ 125 - 24
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 you need a custom search condition, see `linear_search_reverse_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 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 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.
 
-	# Examples
+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)

+ 35 - 0
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)
+}