[m-rev.] for review: add foldl_corresponding/5 and foldl2_corresponding/7 for arrays

Julien Fischer jfischer at opturion.com
Mon Aug 13 17:32:34 AEST 2018


For review by anyone.

Note that test coverge for the many of the higher-order ops in the array
module doesn't appear to be very good; I intend to extend the test case
below to cover any operations that have previously been missed once this
is committed.

Julien.

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

Add foldl_corresponding/5 and foldl2_corrresponding/7 for arrays.

library/array.m:
     Add the above predicates.

NEWS:
     Announce the additions.

tests/hard_coded/Mmakefile:
tests/hard_coded/ho_array_ops.{m,exp}:
     Add tests of the new predicates.

diff --git a/NEWS b/NEWS
index 828da36..393d475 100644
--- a/NEWS
+++ b/NEWS
@@ -448,12 +448,14 @@ Changes to the Mercury standard library:
     - snoc/3
     - find_first_match/3

-* The following function has been added to the array module:
+* The following functions and predicates have been added to the array module:

     - det_least_index/1
     - semidet_least_index/1
     - det_greatest_index/1
     - semidet_greatest_index/1
+   - foldl_corresponding/5
+   - foldl2_corresponding/7

     The following functions in the array module have been deprecated:

diff --git a/library/array.m b/library/array.m
index 3350756..2602c60 100644
--- a/library/array.m
+++ b/library/array.m
@@ -595,6 +595,8 @@
      pred(in, in, out, in, out, in, out, in, out, di, uo) is semidet,
      in, in, out, in, out, in, out, in, out, di, uo) is semidet.

+%---------------------%
+
      % foldr(Fn, Array, X) is equivalent to
      %   list.foldr(Fn, to_list(Array), X)
      % but more efficient.
@@ -690,6 +692,47 @@
      pred(in, in, out, in, out, in, out, in, out, di, uo) is semidet,
      in, in, out, in, out, in, out, in, out, di, uo) is semidet.

+%---------------------%
+
+    % 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.
+    %
+:- pred foldl_corresponding(pred(T1, T2, T3, T3), array(T1), array(T2),
+    T3, T3).
+:- mode foldl_corresponding(in(pred(in, in, in, out) is det), in, in,
+    in, out) is det.
+:- mode foldl_corresponding(in(pred(in, in, mdi, muo) is det), in, in,
+    mdi, muo) is det.
+:- mode foldl_corresponding(in(pred(in, in, di, uo) is det), in, in,
+    di, uo) is det.
+:- mode foldl_corresponding(in(pred(in, in, in, out) is semidet), in, in,
+    in, out) is semidet.
+:- mode foldl_corresponding(in(pred(in, in, mdi, muo) is semidet), in, in,
+    mdi, muo) is semidet.
+:- mode foldl_corresponding(in(pred(in, in, di, uo) is semidet), in, in,
+    di, uo) is semidet.
+
+    % As above, but with two accumulators.
+    %
+:- pred foldl2_corresponding(pred(T1, T2, T3, T3, T4, T4),
+    array(T1), array(T2), T3, T3, T4, T4).
+:- mode foldl2_corresponding(in(pred(in, in, in, out, in, out) is det),
+    in, in, in, out, in, out) is det.
+:- mode foldl2_corresponding(in(pred(in, in, in, out, mdi, muo) is det),
+    in, in, in, out, mdi, muo) is det.
+:- mode foldl2_corresponding(in(pred(in, in, in, out, di, uo) is det),
+    in, in, in, out, di, uo) is det.
+:- mode foldl2_corresponding(in(pred(in, in, in, out, in, out) is semidet),
+    in, in, in, out, in, out) is semidet.
+:- mode foldl2_corresponding(in(pred(in, in, in, out, mdi, muo) is semidet),
+    in, in, in, out, mdi, muo) is semidet.
+:- mode foldl2_corresponding(in(pred(in, in,in, out,  di, uo) is semidet),
+    in, in, in, out, di, uo) is semidet.
+
+%---------------------%
+
      % map_foldl(P, A, B, !Acc):
      % Invoke P(Aelt, Belt, !Acc) on each element of the A array,
      % and construct array B from the resulting values of Belt.
@@ -704,6 +747,8 @@
  :- mode map_foldl(in(pred(in, out, in, out) is semidet),
      in, array_uo, in, out) is semidet.

+%---------------------%
+
      % map_corresponding_foldl(P, A, B, C, !Acc):
      %
      % Given two arrays A and B, invoke P(Aelt, Belt, Celt, !Acc) on
@@ -729,6 +774,8 @@
      in(pred(in, in, out, in, out) is semidet),
      in, in, array_uo, in, out) is semidet.

+%---------------------%
+
      % all_true(Pred, Array):
      % True iff Pred is true for every element of Array.
      %
@@ -2772,6 +2819,72 @@ do_foldr5(P, Min, I, A, !Acc1, !Acc2, !Acc3, !Acc4, !Acc5) :-

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

+foldl_corresponding(P, A, B, !Acc) :-
+    MaxA = array.max(A),
+    MaxB = array.max(B),
+    ( if MaxA = MaxB then
+        do_foldl_corresponding(P, 0, MaxA, A, B, !Acc)
+    else
+        error("array.foldl_corresponding: array arguments differ in size")
+    ).
+
+:- pred do_foldl_corresponding(pred(T1, T2, T3, T3), int, int,
+    array(T1), array(T2), T3, T3).
+:- mode do_foldl_corresponding(in(pred(in, in, in, out) is det), in, in,
+    in, in, in, out) is det.
+:- mode do_foldl_corresponding(in(pred(in, in, mdi, muo) is det), in, in,
+    in, in, mdi, muo) is det.
+:- mode do_foldl_corresponding(in(pred(in, in, di, uo) is det), in, in,
+    in, in, di, uo) is det.
+:- mode do_foldl_corresponding(in(pred(in, in, in, out) is semidet), in, in,
+    in, in, in, out) is semidet.
+:- mode do_foldl_corresponding(in(pred(in, in, mdi, muo) is semidet), in, in,
+    in, in, mdi, muo) is semidet.
+:- mode do_foldl_corresponding(in(pred(in, in, di, uo) is semidet), in, in,
+    in, in, di, uo) is semidet.
+
+do_foldl_corresponding(P, I, Max, A, B, !Acc) :-
+    ( if Max < I then
+        true
+    else
+        P(A ^ unsafe_elem(I), B ^ unsafe_elem(I), !Acc),
+        do_foldl_corresponding(P, I + 1, Max, A, B, !Acc)
+    ).
+
+foldl2_corresponding(P, A, B, !Acc1, !Acc2) :-
+    MaxA = array.max(A),
+    MaxB = array.max(B),
+    ( if MaxA = MaxB then
+        do_foldl2_corresponding(P, 0, MaxA, A, B, !Acc1, !Acc2)
+    else
+        error("array.foldl2_corresponding: array arguments differ in size")
+    ).
+
+:- pred do_foldl2_corresponding(pred(T1, T2, T3, T3, T4, T4), int, int,
+    array(T1), array(T2), T3, T3, T4, T4).
+:- mode do_foldl2_corresponding(in(pred(in, in, in, out, in, out) is det),
+    in, in, in, in, in, out, in, out) is det.
+:- mode do_foldl2_corresponding(in(pred(in, in, in, out, mdi, muo) is det),
+    in, in, in, in, in, out, mdi, muo) is det.
+:- mode do_foldl2_corresponding(in(pred(in, in, in, out, di, uo) is det),
+    in, in, in, in, in, out, di, uo) is det.
+:- mode do_foldl2_corresponding(in(pred(in, in, in, out, in, out) is semidet),
+    in, in, in, in, in, out, in, out) is semidet.
+:- mode do_foldl2_corresponding(in(pred(in, in, in, out, mdi, muo) is semidet),
+    in, in, in, in, in, out, mdi, muo) is semidet.
+:- mode do_foldl2_corresponding(in(pred(in, in, in, out, di, uo) is semidet),
+    in, in, in, in, in, out, di, uo) is semidet.
+
+do_foldl2_corresponding(P, I, Max, A, B, !Acc1, !Acc2) :-
+    ( if Max < I then
+        true
+    else
+        P(A ^ unsafe_elem(I), B ^ unsafe_elem(I), !Acc1, !Acc2),
+        do_foldl2_corresponding(P, I + 1, Max, A, B, !Acc1, !Acc2)
+    ).
+
+%---------------------------------------------------------------------------%
+
  map_foldl(P, A, B, !Acc) :-
      N = array.size(A),
      ( if N =< 0 then
@@ -2804,6 +2917,8 @@ map_foldl_2(P, I, A, !B, !Acc) :-
          true
      ).

+%---------------------------------------------------------------------------%
+
  map_corresponding_foldl(P, A, B, C, !Acc) :-
      N = array.size(A),
      ( if N =< 0 then
diff --git a/tests/hard_coded/Mmakefile b/tests/hard_coded/Mmakefile
index b997253..a418d8c 100644
--- a/tests/hard_coded/Mmakefile
+++ b/tests/hard_coded/Mmakefile
@@ -693,6 +693,7 @@ ifeq "$(findstring profdeep,$(GRADE))" ""
  		cmp_uint8 \
  		dir_test \
  		final_excp \
+		ho_array_ops \
  		int8_static_data \
  		init_excp \
  		intermod_try_goal \
diff --git a/tests/hard_coded/ho_array_ops.exp b/tests/hard_coded/ho_array_ops.exp
index e69de29..15c59cc 100644
--- a/tests/hard_coded/ho_array_ops.exp
+++ b/tests/hard_coded/ho_array_ops.exp
@@ -0,0 +1,36 @@
+TESTING: foldl_corresponding (ok)
+1 - 2
+2 - 4
+3 - 6
+4 - 8
+5 - 10
+RESULT: OK
+FINISHED TESTING: foldl_corresponding (ok)
+
+TESTING: foldl_corresponding (empty)
+RESULT: OK
+FINISHED TESTING: foldl_corresponding (empty)
+
+TESTING: foldl_corresponding (mismatch)
+RESULT: EXCEPTION: software_error("array.foldl_corresponding: array arguments differ in size")
+FINISHED TESTING: foldl_corresponding (mismatch)
+
+TESTING: foldl2_corresponding (ok)
+1 - 2
+2 - 4
+3 - 6
+4 - 8
+5 - 10
+Sum is 45.
+RESULT: OK
+FINISHED TESTING: foldl2_corresponding (ok)
+
+TESTING: foldl2_corresponding (empty)
+Sum is 0.
+RESULT: OK
+FINISHED TESTING: foldl2_corresponding (empty)
+
+TESTING: foldl2_corresponding (mismatch)
+RESULT: EXCEPTION: software_error("array.foldl2_corresponding: array arguments differ in size")
+FINISHED TESTING: foldl2_corresponding (mismatch)
+
diff --git a/tests/hard_coded/ho_array_ops.m b/tests/hard_coded/ho_array_ops.m
index e69de29..edae85e 100644
--- a/tests/hard_coded/ho_array_ops.m
+++ b/tests/hard_coded/ho_array_ops.m
@@ -0,0 +1,127 @@
+%---------------------------------------------------------------------------%
+% vim: ft=mercury ts=4 sw=4 et wm=0 tw=0
+%---------------------------------------------------------------------------%
+%
+% Tests of various higher-order array operations.
+% XXX this should extended cover other ho array operations.
+%
+%---------------------------------------------------------------------------%
+
+:- module ho_array_ops.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is cc_multi.
+
+%---------------------------------------------------------------------------%
+%---------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module array.
+:- import_module int.
+:- import_module list.
+:- import_module string.
+
+%---------------------------------------------------------------------------%
+
+main(!IO) :-
+    do_test("foldl_corresponding (ok)", foldl_corresponding_ok, !IO),
+    do_test("foldl_corresponding (empty)", foldl_corresponding_empty, !IO),
+    do_test("foldl_corresponding (mismatch)", foldl_corresponding_mismatch,
+        !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).
+
+%---------------------------------------------------------------------------%
+
+:- pred do_test(string::in, pred(io, io)::in(pred(di, uo) is det),
+    io::di, io::uo) is cc_multi.
+
+do_test(Desc, Pred, !IO) :-
+    io.format("TESTING: %s\n", [s(Desc)], !IO),
+    ( try [io(!IO)] (
+        Pred(!IO)
+    )
+    then
+        io.write_string("RESULT: OK\n", !IO)
+    catch_any Excp ->
+        io.write_string("RESULT: EXCEPTION: ", !IO),
+        io.write_line(Excp, !IO)
+    ),
+    io.format("FINISHED TESTING: %s\n\n", [s(Desc)], !IO).
+
+%---------------------------------------------------------------------------%
+%
+% Tests for array.foldl_corresponding/5.
+%
+
+:- pred foldl_corresponding_ok(io::di, io::uo) is det.
+
+foldl_corresponding_ok(!IO) :-
+    A = array.from_list([1, 2, 3, 4, 5]),
+    B = array.from_list([2, 4, 6, 8, 10]),
+    array.foldl_corresponding(print_corresponding, A, B, !IO).
+
+:- pred foldl_corresponding_empty(io::di, io::uo) is det.
+
+foldl_corresponding_empty(!IO) :-
+    make_empty_array(A : array(int)),
+    make_empty_array(B : array(int)),
+    array.foldl_corresponding(print_corresponding, A, B, !IO).
+
+:- pred foldl_corresponding_mismatch(io::di, io::uo) is det.
+
+foldl_corresponding_mismatch(!IO) :-
+    make_empty_array(A : array(int)),
+    B = array.from_list([2, 4, 6, 8, 10]),
+    array.foldl_corresponding(print_corresponding, A, B, !IO).
+
+:- pred print_corresponding(int::in, int::in, io::di, io::uo) is det.
+
+print_corresponding(A, B, !IO) :-
+    io.format("%d - %d\n", [i(A), i(B)], !IO).
+
+%---------------------------------------------------------------------------%
+
+:- pred foldl2_corresponding_ok(io::di, io::uo) is det.
+
+foldl2_corresponding_ok(!IO) :-
+    A = array.from_list([1, 2, 3, 4, 5]),
+    B = array.from_list([2, 4, 6, 8, 10]),
+    array.foldl2_corresponding(print_and_sum_corresponding, A, B, 0, Sum,
+        !IO),
+    io.format("Sum is %d.\n", [i(Sum)], !IO).
+
+:- pred foldl2_corresponding_empty(io::di, io::uo) is det.
+
+foldl2_corresponding_empty(!IO) :-
+    make_empty_array(A : array(int)),
+    make_empty_array(B : array(int)),
+    array.foldl2_corresponding(print_and_sum_corresponding, A, B, 0, Sum,
+        !IO),
+    io.format("Sum is %d.\n", [i(Sum)], !IO).
+
+:- pred foldl2_corresponding_mismatch(io::di, io::uo) is det.
+
+foldl2_corresponding_mismatch(!IO) :-
+    make_empty_array(A : array(int)),
+    B = array.from_list([2, 4, 6, 8, 10]),
+    array.foldl2_corresponding(print_and_sum_corresponding, A, B, 0, Sum,
+        !IO),
+    io.format("Sum is %d.\n", [i(Sum)], !IO).
+
+:- pred print_and_sum_corresponding(int::in, int::in, int::in, int::out,
+    io::di, io::uo) is det.
+
+print_and_sum_corresponding(A, B, !Sum, !IO) :-
+    !:Sum = !.Sum + A + B,
+    io.format("%d - %d\n", [i(A), i(B)], !IO).
+
+%---------------------------------------------------------------------------%
+:- end_module ho_array_ops.
+%---------------------------------------------------------------------------%



More information about the reviews mailing list