[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