[m-rev.] for review: change semantics of array.map_corresponding_foldl/6

Julien Fischer jfischer at opturion.com
Tue Aug 14 10:15:29 AEST 2018


For review by anyone.

This build on my earlier uncommitted change to the array module.

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

Change semantics of array.map_corresponding_foldl/6.

library/array.m:
     Make array.map_corresponding_foldl/6 throw an exception if the input array
     differ in size.   This brings its behaviour into line with that of the
     other "corresponding" predicates.  It also avoids the unsafe behaviour
     that can currently result when the second input array has fewer elements
     than the first.

     Add some additional modes to array.map_corresponding_foldl/6.

     Replace calls to error/1 with calls to unexpected/2 throughout this
     module.

NEWS:
     Announce the above change.

tests/hard_coded/ho_array_ops.{m,exp}:
     Extend this test to cover map_corresponding_foldl/6.

Julien.

diff --git a/NEWS b/NEWS
index 393d475..5eeff70 100644
--- a/NEWS
+++ b/NEWS
@@ -117,6 +117,9 @@ Changes that may break compatibility:
    if its argument is equal to int.min_int.  The old behaviour of this function
    is provided by the the new function int.unchecked_abs/1.

+* We have changed the semantics of array.map_corresponding_foldl/6 so that it
+  throws an exception if the input arrays differ in size.
+
  Changes to the Mercury language:

  * We have added a new primitive type, uint, which is an unsigned integer type
diff --git a/library/array.m b/library/array.m
index 2602c60..f02a9dd 100644
--- a/library/array.m
+++ b/library/array.m
@@ -697,7 +697,7 @@
      % foldl_corresponding(P, A, B, !Acc):
      %
      % Does the same job as foldl, but works on two arrays in parallel.
-    % An exception is raised if the array arguments differ in size.
+    % Throws an exception if the array arguments differ in size.
      %
  :- pred foldl_corresponding(pred(T1, T2, T3, T3), array(T1), array(T2),
      T3, T3).
@@ -756,8 +756,7 @@
      % from the result Celt values. Return C and the final value of the
      % accumulator.
      %
-    % C will have as many elements as A does. In most uses, B will also have
-    % this many elements, but may have more; it may NOT have fewer.
+    % Throws an exception if A and B differ in size.
      %
  :- pred map_corresponding_foldl(pred(T1, T2, T3, T4, T4),
      array(T1), array(T2), array(T3), T4, T4).
@@ -773,6 +772,12 @@
  :- mode map_corresponding_foldl(
      in(pred(in, in, out, in, out) is semidet),
      in, in, array_uo, in, out) is semidet.
+:- mode map_corresponding_foldl(
+    in(pred(in, in, out, mdi, muo) is semidet),
+    in, in, array_uo, mdi, muo) is semidet.
+:- mode map_corresponding_foldl(
+    in(pred(in, in, out, di, uo) is semidet),
+    in, in, array_uo, di, uo) is semidet.

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

@@ -1379,7 +1384,7 @@ init(N, X) = A :-

  init(Size, Item, Array) :-
      ( if Size < 0 then
-        error("array.init: negative size")
+        unexpected($pred, "negative size")
      else
          array.init_2(Size, Item, Array)
      ).
@@ -1464,7 +1469,7 @@ generate(Size, GenFunc) = Array :-
      compare(Result, Size, 0),
      (
          Result = (<),
-        error("array.generate: negative size")
+        unexpected($pred, "negative size")
      ;
          Result = (=),
          make_empty_array(Array)
@@ -1529,7 +1534,7 @@ generate_foldl(Size, GenPred, Array, !Acc) :-
      compare(Result, Size, 0),
      (
          Result = (<),
-        error("array.generate_foldl: negative size")
+        unexpected($pred, "negative size")
      ;
          Result = (=),
          make_empty_array(Array)
@@ -2002,7 +2007,7 @@ shrink(!.Array, N) = !:Array :-
  shrink(Size, !Array) :-
      OldSize = array.size(!.Array),
      ( if Size > OldSize then
-        error("array.shrink: can't shrink to a larger size")
+        unexpected($pred, "cannot shrink to a larger size")
      else if Size = OldSize then
          true
      else
@@ -2223,7 +2228,7 @@ fetch_items(Array, Low, High, List) :-
      then
          List = do_foldr_func(func(X, Xs) = [X | Xs], Array, [], Low, High)
      else
-        error("array.fetch_items/4: One or more index is out of bounds")
+        unexpected($pred, "one or more indexes is out-of-bounds")
      ).

  %---------------------------------------------------------------------------%
@@ -2825,7 +2830,7 @@ foldl_corresponding(P, A, B, !Acc) :-
      ( if MaxA = MaxB then
          do_foldl_corresponding(P, 0, MaxA, A, B, !Acc)
      else
-        error("array.foldl_corresponding: array arguments differ in size")
+        unexpected($pred, "mismatched array sizes")
      ).

  :- pred do_foldl_corresponding(pred(T1, T2, T3, T3), int, int,
@@ -2857,7 +2862,7 @@ foldl2_corresponding(P, A, B, !Acc1, !Acc2) :-
      ( if MaxA = MaxB then
          do_foldl2_corresponding(P, 0, MaxA, A, B, !Acc1, !Acc2)
      else
-        error("array.foldl2_corresponding: array arguments differ in size")
+        unexpected($pred, "mismatched array sizes")
      ).

  :- pred do_foldl2_corresponding(pred(T1, T2, T3, T3, T4, T4), int, int,
@@ -2920,15 +2925,18 @@ map_foldl_2(P, I, A, !B, !Acc) :-
  %---------------------------------------------------------------------------%

  map_corresponding_foldl(P, A, B, C, !Acc) :-
-    N = array.size(A),
-    ( if N =< 0 then
+    SizeA = array.size(A),
+    SizeB = array.size(B),
+    ( if SizeA \= SizeB then
+        unexpected($pred, "mismatched array sizes")
+    else if SizeA =< 0 then
          C = array.make_empty_array
      else
          array.unsafe_lookup(A, 0, X),
          array.unsafe_lookup(B, 0, Y),
          P(X, Y, Z, !Acc),
-        C1 = array.init(N, Z),
-        map_corresponding_foldl_2(P, 1, N, A, B, C1, C, !Acc)
+        C1 = array.init(SizeA, Z),
+        map_corresponding_foldl_2(P, 1, SizeA, A, B, C1, C, !Acc)
      ).

  :- pred map_corresponding_foldl_2(pred(T1, T2, T3, T4, T4),
@@ -2945,6 +2953,12 @@ map_corresponding_foldl(P, A, B, C, !Acc) :-
  :- mode map_corresponding_foldl_2(
      in(pred(in, in, out, in, out) is semidet),
      in, in, in, in, array_di, array_uo, in, out) is semidet.
+:- mode map_corresponding_foldl_2(
+    in(pred(in, in, out, mdi, muo) is semidet),
+    in, in, in, in, array_di, array_uo, mdi, muo) is semidet.
+:- mode map_corresponding_foldl_2(
+    in(pred(in, in, out, di, uo) is semidet),
+    in, in, in, in, array_di, array_uo, di, uo) is semidet.

  map_corresponding_foldl_2(P, I, N, A, B, !C, !Acc) :-
      ( if I < N then
@@ -3275,7 +3289,7 @@ least_index(A) = array.min(A).

  det_least_index(A) = Index :-
      ( if array.is_empty(A) then
-        error("array.det_least_index: empty array")
+        unexpected($pred, "empty array")
      else
          Index = array.min(A)
      ).
@@ -3293,7 +3307,7 @@ greatest_index(A) = array.max(A).

  det_greatest_index(A) = Index :-
      ( if array.is_empty(A) then
-        error("array.det_greatest_index: empty array")
+        unexpected($pred, "empty array")
      else
          Index = array.max(A)
      ).
diff --git a/tests/hard_coded/ho_array_ops.exp b/tests/hard_coded/ho_array_ops.exp
index 15c59cc..e6c0289 100644
--- a/tests/hard_coded/ho_array_ops.exp
+++ b/tests/hard_coded/ho_array_ops.exp
@@ -12,7 +12,7 @@ RESULT: OK
  FINISHED TESTING: foldl_corresponding (empty)

  TESTING: foldl_corresponding (mismatch)
-RESULT: EXCEPTION: software_error("array.foldl_corresponding: array arguments differ in size")
+RESULT: EXCEPTION: software_error("predicate `array.foldl_corresponding\'/5: Unexpected: mismatched array sizes")
  FINISHED TESTING: foldl_corresponding (mismatch)

  TESTING: foldl2_corresponding (ok)
@@ -31,6 +31,27 @@ RESULT: OK
  FINISHED TESTING: foldl2_corresponding (empty)

  TESTING: foldl2_corresponding (mismatch)
-RESULT: EXCEPTION: software_error("array.foldl2_corresponding: array arguments differ in size")
+RESULT: EXCEPTION: software_error("predicate `array.foldl2_corresponding\'/7: Unexpected: mismatched array sizes")
  FINISHED TESTING: foldl2_corresponding (mismatch)

+TESTING: map_corresponding_foldl (ok)
+1 + 2 = 3
+2 + 4 = 6
+3 + 6 = 9
+4 + 8 = 12
+5 + 10 = 15
+array([3, 6, 9, 12, 15])
+
+RESULT: OK
+FINISHED TESTING: map_corresponding_foldl (ok)
+
+TESTING: map_corresponding_foldl (empty)
+array([])
+
+RESULT: OK
+FINISHED TESTING: map_corresponding_foldl (empty)
+
+TESTING: map_corresponding_foldl (mismatch)
+RESULT: EXCEPTION: software_error("predicate `array.map_corresponding_foldl\'/6: Unexpected: mismatched array sizes")
+FINISHED TESTING: map_corresponding_foldl (mismatch)
+
diff --git a/tests/hard_coded/ho_array_ops.m b/tests/hard_coded/ho_array_ops.m
index edae85e..b58db23 100644
--- a/tests/hard_coded/ho_array_ops.m
+++ b/tests/hard_coded/ho_array_ops.m
@@ -35,7 +35,13 @@ main(!IO) :-
      do_test("foldl2_corresponding (ok)", foldl2_corresponding_ok, !IO),
      do_test("foldl2_corresponding (empty)", foldl2_corresponding_empty, !IO),
      do_test("foldl2_corresponding (mismatch)", foldl2_corresponding_mismatch,
-        !IO).
+        !IO),
+
+    do_test("map_corresponding_foldl (ok)", map_corresponding_foldl_ok, !IO),
+    do_test("map_corresponding_foldl (empty)", map_corresponding_foldl_empty,
+        !IO),
+    do_test("map_corresponding_foldl (mismatch)",
+        map_corresponding_foldl_mismatch, !IO).

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

@@ -123,5 +129,40 @@ print_and_sum_corresponding(A, B, !Sum, !IO) :-
      io.format("%d - %d\n", [i(A), i(B)], !IO).

  %---------------------------------------------------------------------------%
+
+:- pred map_corresponding_foldl_ok(io::di, io::uo) is det.
+
+map_corresponding_foldl_ok(!IO) :-
+    A = array.from_list([1, 2, 3, 4, 5]),
+    B = array.from_list([2, 4, 6, 8, 10]),
+    array.map_corresponding_foldl(print_and_partial_sum, A, B, C, !IO),
+    io.write_line(C, !IO),
+    io.nl(!IO).
+
+:- pred map_corresponding_foldl_empty(io::di, io::uo) is det.
+
+map_corresponding_foldl_empty(!IO) :-
+    make_empty_array(A),
+    make_empty_array(B),
+    array.map_corresponding_foldl(print_and_partial_sum, A, B, C, !IO),
+    io.write_line(C, !IO),
+    io.nl(!IO).
+
+:- pred map_corresponding_foldl_mismatch(io::di, io::uo) is det.
+
+map_corresponding_foldl_mismatch(!IO) :-
+    A = array.from_list([1, 2, 3, 4, 5]),
+    make_empty_array(B),
+    array.map_corresponding_foldl(print_and_partial_sum, A, B, C, !IO),
+    io.write_line(C, !IO),
+    io.nl(!IO).
+
+:- pred print_and_partial_sum(int::in, int::in, int::out, io::di, io::uo) is det.
+
+print_and_partial_sum(A, B, C, !IO) :-
+    C = A + B,
+    io.format("%d + %d = %d\n", [i(A), i(B), i(C)], !IO).
+
+%---------------------------------------------------------------------------%
  :- end_module ho_array_ops.
  %---------------------------------------------------------------------------%


More information about the reviews mailing list