[m-rev.] for review: efficient insertion of duplicate items into bags

Julien Fischer jfischer at opturion.com
Sun May 7 02:30:46 AEST 2017


For review by anyone.

----------

Efficient insertion of duplicate items into bags.

library/bag.m:
     Add predicates and a function for efficiently inserting multiple copies of
     an item into a bag.

NEWS:
     Announce the above additions.

tests/hard_coded/bag_various.{m,exp}:
     Extend this test to cover the above.

Julien.

diff --git a/NEWS b/NEWS
index 9c23b12f0..e1987374c 100644
--- a/NEWS
+++ b/NEWS
@@ -446,6 +446,12 @@ Changes to the Mercury standard library:
    - return_sccs_in_from_to_order/1
    - return_sccs_in_to_from_order/1

+* We have added the following predicates and function to the bag module:
+
+  - insert_duplicates/4
+  - det_insert_duplicates/4
+  - det_insert_duplicates/3
+
  Changes to the Mercury compiler:

  * We have added a new option --warn-dead-preds. While the existing option
diff --git a/library/bag.m b/library/bag.m
index 2cd115342..968725e26 100644
--- a/library/bag.m
+++ b/library/bag.m
@@ -83,6 +83,17 @@
  :- func insert_list(bag(T), list(T)) = bag(T).
  :- pred insert_list(list(T)::in, bag(T)::in, bag(T)::out) is det.

+    % Insert N copies of a particular value into a bag.
+    % Fails if N < 0.
+    %
+:- pred insert_duplicates(int::in, T::in, bag(T)::in, bag(T)::out)
+    is semidet.
+
+    % As above, but throws an exception if N < 0.
+    %
+:- func det_insert_duplicates(bag(T), int, T) = bag(T).
+:- pred det_insert_duplicates(int::in, T::in, bag(T)::in, bag(T)::out) is det.
+
      % Insert a set of values into a bag.
      %
  :- func insert_set(bag(T), set(T)) = bag(T).
@@ -433,6 +444,32 @@ insert_list([Item | Items], !Bag) :-
      bag.insert(Item, !Bag),
      bag.insert_list(Items, !Bag).

+insert_duplicates(N, Item, bag(!.Map), bag(!:Map)) :-
+    compare(CmpResult, N, 0),
+    (
+        CmpResult = (>),
+        ( if map.search(!.Map, Item, Count) then
+            map.det_update(Item, Count + N, !Map)
+        else
+            map.det_insert(Item, N, !Map)
+        )
+    ;
+        CmpResult = (=)
+    ;
+        CmpResult = (<),
+        fail
+    ).
+
+det_insert_duplicates(!.Bag, N, Item) = !:Bag :-
+    det_insert_duplicates(N, Item, !Bag).
+
+det_insert_duplicates(N, Item, !Bag) :-
+    ( if insert_duplicates(N, Item, !Bag) then
+        true
+    else
+        error("bag.det_insert_duplicates: number of items is negative")
+    ).
+
  insert_set(!.Bag, Xs) = !:Bag :-
      bag.insert_set(Xs, !Bag).

diff --git a/tests/hard_coded/bag_various.exp b/tests/hard_coded/bag_various.exp
index e8ae4c23e..3e8ef8510 100644
--- a/tests/hard_coded/bag_various.exp
+++ b/tests/hard_coded/bag_various.exp
@@ -5,3 +5,7 @@ bag.count_unique: 4
  bag.member(4): yes
  bag.member(5): no
  unsorted_solutions(bag.member/3): [{4, [1, 1, 1, 2, 3, 3]}, {3, [1, 1, 1, 2, 3, 4]}, {3, [1, 1, 1, 2, 3, 4]}, {2, [1, 1, 1, 3, 3, 4]}, {1, [1, 1, 2, 3, 3, 4]}, {1, [1, 1, 2, 3, 3, 4]}, {1, [1, 1, 2, 3, 3, 4]}]
+bag.insert_duplicates(5, "foo"): ["foo", "foo", "foo", "foo", "foo"]
+bag.insert_duplicates(0, "foo"): []
+bag.insert_duplicates(-1, "foo"): "fail"
+bag.insert_duplicates(4, "foo"): ["foo", "foo", "foo", "foo", "foo"]
diff --git a/tests/hard_coded/bag_various.m b/tests/hard_coded/bag_various.m
index f1e96b73e..c3eb24b48 100644
--- a/tests/hard_coded/bag_various.m
+++ b/tests/hard_coded/bag_various.m
@@ -39,11 +39,28 @@ main(!IO) :-
          O = {M, []++to_list(Rest)}
      ), Sols),
      dump("unsorted_solutions(bag.member/3): ", Sols, !IO),
+
+    test_insert_duplicates(5, bag.init, !IO),
+    test_insert_duplicates(0, bag.init, !IO),
+    test_insert_duplicates(-1, bag.init, !IO),
+    test_insert_duplicates(4, bag.from_list(["foo"]), !IO),
+
      true.

+:- pred test_insert_duplicates(int::in, bag(string)::in, io::di, io::uo)
+    is det.
+
+test_insert_duplicates(N, Bag0, !IO) :-
+    Prefix = "bag.insert_duplicates(" ++ int_to_string(N) ++ ", \"foo\"): ",
+    ( if bag.insert_duplicates(N, "foo", Bag0, Bag) then
+        bag.to_list(Bag, List),
+        dump(Prefix, List, !IO)
+    else
+        dump(Prefix, "fail", !IO)
+    ).
+
  :- pred dump(string::in, T::in, io::di, io::uo) is det.

  dump(Msg, T, !IO) :-
-    io__write_string(Msg, !IO),
-    io__write(T, !IO),
-    io__nl(!IO).
+    io.write_string(Msg, !IO),
+    io.write_line(T, !IO).


More information about the reviews mailing list