[m-rev.] for review add ranges.search_member/4

Julien Fischer jfischer at opturion.com
Sat Mar 16 17:08:34 AEDT 2024


For review by anyone.

The main thing I would like is feedback on is the name of the new
prediate. (Also, the test suite does not currently appear to contain
any specific tests of the ranges module; I intend to extend the test
case included in this diff to cover the rest of the ranges module in a
separate change.)

----------------------

Add ranges.search_member/4.

Add a predicate to search a ranges for a value and return the endpoints
of the range (if any) that the value is contained in.

library/ranges.m:
     Add the new predicate.

NEWS.md:
     Announce the addition.

tests/hard_coded/Mmakefile:
tests/hard_coded/test_ranges.{m,exp}:
     Add a test case.

Julien.

diff --git a/NEWS.md b/NEWS.md
index fe4211b..3eb4db8 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -686,6 +686,12 @@ Changes to the Mercury standard library
      - pred `permutation/4`
              (replacement: `random.shuffle_list/4` or `random.shuffle_list/5`)

+### Changes to the `ranges` module
+
+* The following predicate has been added:
+
+    - pred `search_member/4`
+
  ### Changes to the `rbtree` module

  * The following predicates and functions have been added:
diff --git a/library/ranges.m b/library/ranges.m
index e29ed0b..9bd92e8 100644
--- a/library/ranges.m
+++ b/library/ranges.m
@@ -3,7 +3,7 @@
  %---------------------------------------------------------------------------%
  % Copyright (C) 2006-2009 The University of Melbourne.
  % Copyright (C) 2013-2016 Opturion Pty Ltd.
-% Copyright (C) 2017-2019, 2022 The Mercury team.
+% Copyright (C) 2017-2019, 2022-2024 The Mercury team.
  % This file is distributed under the terms specified in COPYING.LIB.
  %---------------------------------------------------------------------------%
  %
@@ -109,6 +109,12 @@
      %
  :- pred nondet_member(int::out, ranges::in) is nondet.

+    % search_member(N, Set, L, H):
+    % L and H are the endpoints of the range in Set that contains N.
+    % Fails if N is not a member of Set.
+    %
+:- pred search_member(int::in, ranges::in, int::out, int::out) is semidet.
+
      % subset(A, B) is true iff every value in A is in B.
      %
  :- pred subset(ranges::in, ranges::in) is semidet.
@@ -525,12 +531,27 @@ range_member(L, U, range(A0, A1, As)) :-
          range_member(L, U, As)
      ).

+%---------------------------------------------------------------------------%
+
  nondet_member(N, As) :-
      range_member(L, U, As),
      int.nondet_int_in_range(L, U, N).

  %---------------------------------------------------------------------------%

+search_member(N, range(L0, H0, Rest), L, H) :-
+    ( if
+        N > L0,
+        N =< H0
+    then
+        L = L0 + 1,
+        H = H0
+    else
+        search_member(N, Rest, L, H)
+    ).
+
+%---------------------------------------------------------------------------%
+
  subset(A, B) :-
      % XXX Should implement this more efficiently.
      ranges.difference(A, B) = nil.
diff --git a/tests/hard_coded/Mmakefile b/tests/hard_coded/Mmakefile
index 2fb0b81..558ff02 100644
--- a/tests/hard_coded/Mmakefile
+++ b/tests/hard_coded/Mmakefile
@@ -458,6 +458,7 @@ ORDINARY_PROGS = \
  	test_one_or_more_chunk \
  	test_pretty_printer \
  	test_pretty_printer_defaults \
+	test_ranges \
  	test_semaphore \
  	test_split_switch_arms \
  	test_yield \
diff --git a/tests/hard_coded/test_ranges.exp b/tests/hard_coded/test_ranges.exp
index e69de29..1253f8c 100644
--- a/tests/hard_coded/test_ranges.exp
+++ b/tests/hard_coded/test_ranges.exp
@@ -0,0 +1,9 @@
+search_member(0, Empty) ==> FAIL
+search_member(-1, TestRanges) ==> FAIL
+search_member(0, TestRanges) ==> FAIL
+search_member(1558, TestRanges) ==> FAIL
+search_member(1560, TestRanges) ==> (1559, 8460).
+search_member(8461, TestRanges) ==> FAIL
+search_member(95586, TestRanges) ==> (93719, 100620).
+search_member(100620, TestRanges) ==> (93719, 100620).
+search_member(100621, TestRanges) ==> FAIL
diff --git a/tests/hard_coded/test_ranges.m b/tests/hard_coded/test_ranges.m
index e69de29..4b31109 100644
--- a/tests/hard_coded/test_ranges.m
+++ b/tests/hard_coded/test_ranges.m
@@ -0,0 +1,53 @@
+%---------------------------------------------------------------------------%
+% vim: ts=4 sw=4 et ft=mercury
+%---------------------------------------------------------------------------%
+
+:- module test_ranges.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+%---------------------------------------------------------------------------%
+%---------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module list.
+:- import_module ranges.
+:- import_module string.
+
+%---------------------------------------------------------------------------%
+
+main(!IO) :-
+    test_search_member(!IO).
+
+%---------------------------------------------------------------------------%
+
+:- pred test_search_member(io::di, io::uo) is det.
+
+test_search_member(!IO) :-
+
+    EmptyRanges = ranges.empty : ranges,
+    do_test_search_member(EmptyRanges, "Empty", 0, !IO),
+
+    TestRanges = range(1559, 8460) `union` range(43319, 48780) `union`
+        range(93719, 100620),
+    TestValues = [-1, 0, 1558, 1560, 8461, 95586, 100620, 100621],
+    list.foldl(do_test_search_member(TestRanges, "TestRanges"), TestValues, !IO).
+
+:- pred do_test_search_member(ranges::in, string::in, int::in,
+    io::di, io::uo) is det.
+
+do_test_search_member(Ranges, RangesDesc, N, !IO) :-
+    io.format("search_member(%d, %s) ==> ", [i(N), s(RangesDesc)], !IO),
+    ( if ranges.search_member(N, Ranges, Lo, Hi) then
+        io.format("(%d, %d).\n", [i(Lo), i(Hi)], !IO)
+    else
+        io.print_line("FAIL", !IO)
+    ).
+
+%---------------------------------------------------------------------------%
+:- end_module test_ranges.
+%---------------------------------------------------------------------------%


More information about the reviews mailing list