authorgravatar for brodeuralexis@gmail.comAlexis Brodeur <brodeuralexis@gmail.com> 2022-09-02 20:56:36-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-02-21 12:28:43-05:00
log98dd041d536aad1d4936353b4f5e4a2e0aab0fe1
tree62e966ccc6156110737c9552b33648993c339d97
parent2737dce84f99a05af51b66fc12794b43dc10fa41

Relax `std.sort.binarySearch` requirements

Forcing the key to be of the same type as the sorted items used during the search is a valid use case. There, however, exists some cases where the key and the items are of heterogeneous types, like searching for a code point in ordered ranges of code points: ```zig const CodePoint = u21; const CodePointRange = [2]CodePoint; const valid_ranges = &[_]CodePointRange{ // an ordered array of ranges }; fn orderCodePointAndRange( context: void, code_point: CodePoint, range: CodePointRange ) std.math.Order { _ = context; if (code_point < range[0]) { return .lt; } if (code_point > range[1]) { return .gt; } return .eq; } fn isValidCodePoint(code_point: CodePoint) bool { return std.sort.binarySearch( CodePointRange, code_point, valid_ranges, void, orderCodePointAndRange ) != null; } ``` It is so expected that `std.sort.binarySearch` should therefore support both homogeneous and heterogeneous keys.

1 files changed, 44 insertions(+), 10 deletions(-)

lib/std/sort.zig+44-10
...@@ -6,10 +6,10 @@ const math = std.math;...@@ -6,10 +6,10 @@ const math = std.math;
66
7pub fn binarySearch(7pub fn binarySearch(
8 comptime T: type,8 comptime T: type,
9 key: T,9 key: anytype,
10 items: []const T,10 items: []const T,
11 context: anytype,11 context: anytype,
12 comptime compareFn: fn (context: @TypeOf(context), lhs: T, rhs: T) math.Order,12 comptime compareFn: fn (context: @TypeOf(context), key: @TypeOf(key), mid: T) math.Order,
13) ?usize {13) ?usize {
14 var left: usize = 0;14 var left: usize = 0;
15 var right: usize = items.len;15 var right: usize = items.len;
...@@ -41,35 +41,69 @@ test "binarySearch" {...@@ -41,35 +41,69 @@ test "binarySearch" {
41 };41 };
42 try testing.expectEqual(42 try testing.expectEqual(
43 @as(?usize, null),43 @as(?usize, null),
44 binarySearch(u32, 1, &[_]u32{}, {}, S.order_u32),44 binarySearch(u32, @as(u32, 1), &[_]u32{}, {}, S.order_u32),
45 );45 );
46 try testing.expectEqual(46 try testing.expectEqual(
47 @as(?usize, 0),47 @as(?usize, 0),
48 binarySearch(u32, 1, &[_]u32{1}, {}, S.order_u32),48 binarySearch(u32, @as(u32, 1), &[_]u32{1}, {}, S.order_u32),
49 );49 );
50 try testing.expectEqual(50 try testing.expectEqual(
51 @as(?usize, null),51 @as(?usize, null),
52 binarySearch(u32, 1, &[_]u32{0}, {}, S.order_u32),52 binarySearch(u32, @as(u32, 1), &[_]u32{0}, {}, S.order_u32),
53 );53 );
54 try testing.expectEqual(54 try testing.expectEqual(
55 @as(?usize, null),55 @as(?usize, null),
56 binarySearch(u32, 0, &[_]u32{1}, {}, S.order_u32),56 binarySearch(u32, @as(u32, 0), &[_]u32{1}, {}, S.order_u32),
57 );57 );
58 try testing.expectEqual(58 try testing.expectEqual(
59 @as(?usize, 4),59 @as(?usize, 4),
60 binarySearch(u32, 5, &[_]u32{ 1, 2, 3, 4, 5 }, {}, S.order_u32),60 binarySearch(u32, @as(u32, 5), &[_]u32{ 1, 2, 3, 4, 5 }, {}, S.order_u32),
61 );61 );
62 try testing.expectEqual(62 try testing.expectEqual(
63 @as(?usize, 0),63 @as(?usize, 0),
64 binarySearch(u32, 2, &[_]u32{ 2, 4, 8, 16, 32, 64 }, {}, S.order_u32),64 binarySearch(u32, @as(u32, 2), &[_]u32{ 2, 4, 8, 16, 32, 64 }, {}, S.order_u32),
65 );65 );
66 try testing.expectEqual(66 try testing.expectEqual(
67 @as(?usize, 1),67 @as(?usize, 1),
68 binarySearch(i32, -4, &[_]i32{ -7, -4, 0, 9, 10 }, {}, S.order_i32),68 binarySearch(i32, @as(i32, -4), &[_]i32{ -7, -4, 0, 9, 10 }, {}, S.order_i32),
69 );69 );
70 try testing.expectEqual(70 try testing.expectEqual(
71 @as(?usize, 3),71 @as(?usize, 3),
72 binarySearch(i32, 98, &[_]i32{ -100, -25, 2, 98, 99, 100 }, {}, S.order_i32),72 binarySearch(i32, @as(i32, 98), &[_]i32{ -100, -25, 2, 98, 99, 100 }, {}, S.order_i32),
73 );
74 const R = struct {
75 b: i32,
76 e: i32,
77
78 fn r(b: i32, e: i32) @This() {
79 return @This(){ .b = b, .e = e };
80 }
81
82 fn order(context: void, key: i32, mid: @This()) math.Order {
83 _ = context;
84
85 if (key < mid.b) {
86 return .lt;
87 }
88
89 if (key > mid.e) {
90 return .gt;
91 }
92
93 return .eq;
94 }
95 };
96 try testing.expectEqual(
97 @as(?usize, null),
98 binarySearch(R, @as(i32, -45), &[_]R{ R.r(-100, -50), R.r(-40, -20), R.r(-10, 20), R.r(30, 40) }, {}, R.order),
99 );
100 try testing.expectEqual(
101 @as(?usize, 2),
102 binarySearch(R, @as(i32, 10), &[_]R{ R.r(-100, -50), R.r(-40, -20), R.r(-10, 20), R.r(30, 40) }, {}, R.order),
103 );
104 try testing.expectEqual(
105 @as(?usize, 1),
106 binarySearch(R, @as(i32, -20), &[_]R{ R.r(-100, -50), R.r(-40, -20), R.r(-10, 20), R.r(30, 40) }, {}, R.order),
73 );107 );
74}108}
75109