[m-rev.] for review: add array.swap/4 and array.unsafe_swap/4

Julien Fischer jfischer at opturion.com
Mon Nov 12 17:20:04 AEDT 2018


For review by anyone.

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

Add array.swap/4 and array.unsafe_swap/4.

Report out-of-bounds errors more precisely for array.fill_range/5.

library/array.m:
     As above.

NEWS:
     Announce the addition.

tests/hard_coded/Mmakefile:
tests/hard_coded/array_swap.{m,exp}:
     Add a test for array.swap/4.

tests/hard_coded/array_fill.exp:
     Conform to the second change above.

Julien.

diff --git a/NEWS b/NEWS
index 3f8dd44..88d36a7 100644
--- a/NEWS
+++ b/NEWS
@@ -466,6 +466,8 @@ Changes to the Mercury standard library:
     - foldl2_corresponding/7
     - fill/3
     - fill_range/5
+   - swap/4
+   - unsafe_swap/4

     The following functions in the array module have been deprecated:

diff --git a/library/array.m b/library/array.m
index 91fc907..f990ae2 100644
--- a/library/array.m
+++ b/library/array.m
@@ -333,6 +333,18 @@
  :- func 'unsafe_elem :='(int, array(T), T) = array(T).
  :- mode 'unsafe_elem :='(in, array_di, in) = array_uo is det.

+    % swap(I, J, !Array):
+    % Swap the item in the I'th position with the item in the J'th position.
+    % Throws an exception if either of I or J is out-of-bounds.
+    %
+:- pred swap(int, int, array(T), array(T)).
+:- mode swap(in, in, array_di, array_uo) is det.
+
+    % As above, but omit the bounds check.
+    %
+:- pred unsafe_swap(int, int, array(T), array(T)).
+:- mode unsafe_swap(in, in, array_di, array_uo) is det.
+
      % Returns every element of the array, one by one.
      %
  :- pred member(array(T)::in, T::out) is nondet.
@@ -350,6 +362,7 @@
  %:- mode copy(array_ui) = array_uo is det.
  :- mode copy(in) = array_uo is det.

+
      % resize(Size, Init, Array0, Array):
      % The array is expanded or shrunk to make it fit the new size `Size'.
      % Any new entries are filled with `Init'. Throws an exception if
@@ -2141,9 +2154,9 @@ fill_range(Item, Lo, Hi, !Array) :-
      ( if Lo > Hi then
          unexpected($pred, "empty range")
      else if not in_bounds(!.Array, Lo) then
-        out_of_bounds_error(!.Array, Lo, "fill_range")
+        arg_out_of_bounds_error(!.Array, "second", Lo, "fill_range")
      else if not in_bounds(!.Array, Hi) then
-        out_of_bounds_error(!.Array, Hi, "fill_range")
+        arg_out_of_bounds_error(!.Array, "third", Hi, "fill_range")
      else
          do_fill_range(Item, Lo, Hi, !Array)
      ).
@@ -2429,6 +2442,23 @@ map_2(N, Size, Closure, OldArray, !NewArray) :-

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

+swap(I, J, !Array) :-
+    ( if not in_bounds(!.Array, I) then
+        arg_out_of_bounds_error(!.Array, "first", I, "array.swap")
+    else if not in_bounds(!.Array, J) then
+        arg_out_of_bounds_error(!.Array, "second", J, "array.swap")
+    else
+        unsafe_swap(I, J, !Array)
+    ).
+
+unsafe_swap(I, J, !Array) :-
+    array.unsafe_lookup(!.Array, I, IVal),
+    array.unsafe_lookup(!.Array, J, JVal),
+    array.unsafe_set(I, JVal, !Array),
+    array.unsafe_set(J, IVal, !Array).
+
+%---------------------------------------------------------------------------%
+
  member(A, X) :-
      nondet_int_in_range(array.min(A), array.max(A), N),
      array.unsafe_lookup(A, N, X).
@@ -3398,6 +3428,18 @@ out_of_bounds_error(Array, Index, PredName) :-
          [s(PredName), i(Index), i(Min), i(Max)], Msg),
      throw(array.index_out_of_bounds(Msg)).

+    % Like the above, but for use in cases where the are multiple arguments
+    % that correspond to array indices.
+    %
+:- pred arg_out_of_bounds_error(array(T), string, int, string).
+:- mode arg_out_of_bounds_error(in, in, in, in) is erroneous.
+
+arg_out_of_bounds_error(Array, ArgPosn, Index, PredName) :-
+    array.bounds(Array, Min, Max),
+    string.format("%s argument of %s: index %d not in range [%d, %d]",
+        [s(ArgPosn), s(PredName), i(Index), i(Min), i(Max)], Msg),
+    throw(array.index_out_of_bounds(Msg)).
+
  %---------------------------------------------------------------------------%

  least_index(A) = array.min(A).
diff --git a/tests/hard_coded/Mmakefile b/tests/hard_coded/Mmakefile
index c012795..bbbbc97 100644
--- a/tests/hard_coded/Mmakefile
+++ b/tests/hard_coded/Mmakefile
@@ -675,6 +675,7 @@ ifeq "$(findstring profdeep,$(GRADE))" ""
  		array_fill \
  		array_resize \
  		array_shrink \
+		array_swap \
  		allow_stubs \
  		arith_int16 \
  		arith_int32 \
diff --git a/tests/hard_coded/array_fill.exp b/tests/hard_coded/array_fill.exp
index 85df1f9..dc3f584 100644
--- a/tests/hard_coded/array_fill.exp
+++ b/tests/hard_coded/array_fill.exp
@@ -81,13 +81,13 @@ Array0 = array([1, 2, 3, 4, 5, 6])
  Lo = -1
  Hi = 3
  Fill = 561
-INDEX-OUT-OF-BOUNDS: "fill_range: index -1 not in range [0, 5]"
+INDEX-OUT-OF-BOUNDS: "second argument of fill_range: index -1 not in range [0, 5]"
  -------FILL RANGE-------
  Array0 = array([1, 2, 3, 4, 5, 6])
  Lo = 1
  Hi = 8
  Fill = 561
-INDEX-OUT-OF-BOUNDS: "fill_range: index 8 not in range [0, 5]"
+INDEX-OUT-OF-BOUNDS: "third argument of fill_range: index 8 not in range [0, 5]"
  -------FILL RANGE-------
  Array0 = array([1, 2, 3, 4, 5, 6])
  Lo = 1
@@ -99,4 +99,4 @@ Array0 = array([])
  Lo = 0
  Hi = 0
  Fill = 561
-INDEX-OUT-OF-BOUNDS: "fill_range: index 0 not in range [0, -1]"
+INDEX-OUT-OF-BOUNDS: "second argument of fill_range: index 0 not in range [0, -1]"
diff --git a/tests/hard_coded/array_swap.exp b/tests/hard_coded/array_swap.exp
new file mode 100644
index 0000000..40e4710
--- /dev/null
+++ b/tests/hard_coded/array_swap.exp
@@ -0,0 +1,72 @@
+================
+Array0 = array([1, 2, 3, 4])
+Swap: -1 <-> 0
+EXCEPTION: "first argument of array.swap: index -1 not in range [0, 3]"
+================
+Array0 = array([1, 2, 3, 4])
+Swap: 2 <-> -1
+EXCEPTION: "second argument of array.swap: index -1 not in range [0, 3]"
+================
+Array0 = array([1, 2, 3, 4])
+Swap: 0 <-> 0
+Array = array([1, 2, 3, 4])
+================
+Array0 = array([1, 2, 3, 4])
+Swap: 1 <-> 2
+Array = array([1, 3, 2, 4])
+================
+Array0 = array([1i8, 2i8, 3i8, 4i8])
+Swap: 0 <-> 1
+Array = array([2i8, 1i8, 3i8, 4i8])
+================
+Array0 = array([1u8, 2u8, 3u8, 4u8])
+Swap: 0 <-> 1
+Array = array([2u8, 1u8, 3u8, 4u8])
+================
+Array0 = array([1i16, 2i16, 3i16, 4i16])
+Swap: 0 <-> 1
+Array = array([2i16, 1i16, 3i16, 4i16])
+================
+Array0 = array([1u16, 2u16, 3u16, 4u16])
+Swap: 0 <-> 1
+Array = array([2u16, 1u16, 3u16, 4u16])
+================
+Array0 = array([1i32, 2i32, 3i32, 4i32])
+Swap: 0 <-> 1
+Array = array([2i32, 1i32, 3i32, 4i32])
+================
+Array0 = array([1u32, 2u32, 3u32, 4u32])
+Swap: 0 <-> 1
+Array = array([2u32, 1u32, 3u32, 4u32])
+================
+Array0 = array([1i64, 2i64, 3i64, 4i64])
+Swap: 0 <-> 1
+Array = array([2i64, 1i64, 3i64, 4i64])
+================
+Array0 = array([1u64, 2u64, 3u64, 4u64])
+Swap: 0 <-> 1
+Array = array([2u64, 1u64, 3u64, 4u64])
+================
+Array0 = array([1.0, 2.0, 3.0, 4.0])
+Swap: 0 <-> 1
+Array = array([2.0, 1.0, 3.0, 4.0])
+================
+Array0 = array(["one", "two", "three", "four"])
+Swap: 0 <-> 1
+Array = array(["two", "one", "three", "four"])
+================
+Array0 = array(['a', 'b', 'c', 'd'])
+Swap: 0 <-> 1
+Array = array(['b', 'a', 'c', 'd'])
+================
+Array0 = array([orange, lemon, apple, pear])
+Swap: 0 <-> 1
+Array = array([lemon, orange, apple, pear])
+================
+Array0 = array([color(1u8, 1u8, 1u8), color(2u8, 2u8, 2u8), color(3u8, 3u8, 3u8), color(4u8, 4u8, 4u8)])
+Swap: 0 <-> 1
+Array = array([color(2u8, 2u8, 2u8), color(1u8, 1u8, 1u8), color(3u8, 3u8, 3u8), color(4u8, 4u8, 4u8)])
+================
+Array0 = array([[1], [2, 2], [3, 3, 3], [4, 4, 4, 4]])
+Swap: 0 <-> 1
+Array = array([[2, 2], [1], [3, 3, 3], [4, 4, 4, 4]])
diff --git a/tests/hard_coded/array_swap.m b/tests/hard_coded/array_swap.m
new file mode 100644
index 0000000..8b76a65
--- /dev/null
+++ b/tests/hard_coded/array_swap.m
@@ -0,0 +1,72 @@
+%---------------------------------------------------------------------------%
+% vim: ft=mercury ts=4 sw=4 et
+%---------------------------------------------------------------------------%
+%
+% Test array.swap/4.
+%
+%---------------------------------------------------------------------------%
+
+:- module array_swap.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is cc_multi.
+
+%---------------------------------------------------------------------------%
+%---------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module array.
+:- import_module exception.
+:- import_module list.
+:- import_module string.
+
+:- type fruit
+    --->    orange
+    ;       lemon
+    ;       apple
+    ;       pear.
+
+:- type color
+    --->    color(uint8, uint8, uint8).
+
+main(!IO) :-
+    test_swap([1, 2, 3, 4], -1, 0, !IO), % Should raise exception.
+    test_swap([1, 2, 3, 4], 2, -1, !IO), % Should raise exception.
+    test_swap([1, 2, 3, 4], 0, 0, !IO),
+    test_swap([1, 2, 3, 4], 1, 2, !IO),
+    test_swap([1i8, 2i8, 3i8, 4i8], 0, 1, !IO),
+    test_swap([1u8, 2u8, 3u8, 4u8], 0, 1, !IO),
+    test_swap([1i16, 2i16, 3i16, 4i16], 0, 1, !IO),
+    test_swap([1u16, 2u16, 3u16, 4u16], 0, 1, !IO),
+    test_swap([1i32, 2i32, 3i32, 4i32], 0, 1, !IO),
+    test_swap([1u32, 2u32, 3u32, 4u32], 0, 1, !IO),
+    test_swap([1i64, 2i64, 3i64, 4i64], 0, 1, !IO),
+    test_swap([1u64, 2u64, 3u64, 4u64], 0, 1, !IO),
+    test_swap([1.0, 2.0, 3.0, 4.0], 0, 1, !IO),
+    test_swap(["one", "two", "three", "four"], 0, 1, !IO),
+    test_swap(['a', 'b', 'c', 'd'], 0, 1, !IO),
+    test_swap([orange, lemon, apple, pear], 0, 1, !IO),
+    test_swap([color(1u8, 1u8, 1u8), color(2u8, 2u8, 2u8),
+        color(3u8, 3u8, 3u8), color(4u8, 4u8, 4u8)], 0, 1, !IO),
+    test_swap([[1], [2, 2], [3, 3, 3], [4, 4, 4, 4]], 0, 1, !IO).
+
+:- pred test_swap(list(T)::in, int::in, int::in, io::di, io::uo) is cc_multi.
+
+test_swap(Elems, I, J, !IO) :-
+    io.write_string("================\n", !IO),
+    array.from_list(Elems, Array0),
+    io.write_string("Array0 = ", !IO),
+    io.write_line(Array0, !IO),
+    io.format("Swap: %d <-> %d\n", [i(I), i(J)], !IO),
+    ( try [] (
+        array.swap(I, J, Array0, Array)
+    ) then
+        io.write_string("Array = ", !IO),
+        io.write_line(Array, !IO)
+    catch index_out_of_bounds(S) ->
+        io.write_string("EXCEPTION: ", !IO),
+        io.write_line(S, !IO)
+    ).


More information about the reviews mailing list