[m-dev.] For review: additions to array.m

Ralph Becket rbeck at microsoft.com
Tue Feb 6 00:11:13 AEDT 2001


I'd like to check this in before the end of today.

Estimated hours taken: 4

Added sorting, fold and permutation functions/predicates to array.m.

library/array.m:
	Added funcs sort/1, foldl/3, foldr/3.
	Added pred permutation/4.

NEWS:
	Recorded above in changes to library section.

Index: array.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/array.m,v
retrieving revision 1.82
diff -u -r1.82 array.m
--- array.m	2001/01/12 14:08:46	1.82
+++ array.m	2001/02/05 13:04:13
@@ -33,7 +33,7 @@
 
 :- module array.
 :- interface.
-:- import_module list, std_util.
+:- import_module list, std_util, random.
 
 :- type array(T).
 
@@ -287,6 +287,39 @@
 :- func array_compare(array(T), array(T)) = comparison_result.
 :- mode array_compare(in, in) = out is det.
 
+	% array__sort(Array) returns a version of Array sorted
+	% into ascending order.
+	%
+:- func array__sort(array(T)) = array(T).
+:- mode array__sort(array_di) = array_uo is det.
+
+	% array__foldl(Fn, Array, X) is equivalent to
+	% 	list__foldl(Fn, array__to_list(Array), X)
+	% but much more efficient.
+	%
+:- func array__foldl(func(T1, T2) = T2, array(T1), T2) = T2.
+:- mode array__foldl(func(in, in) = out is det, array_ui, in) = out is det.
+:- mode array__foldl(func(in, in) = out is det, in, in) = out is det.
+:- mode array__foldl(func(in, di) = uo is det, array_ui, di) = uo is det.
+:- mode array__foldl(func(in, di) = uo is det, in, di) = uo is det.
+
+	% array__foldr(Fn, Array, X) is equivalent to
+	% 	list__foldr(Fn, array__to_list(Array), X)
+	% but much more efficient.
+	%
+:- func array__foldr(func(T1, T2) = T2, array(T1), T2) = T2.
+:- mode array__foldr(func(in, in) = out is det, array_ui, in) = out is det.
+:- mode array__foldr(func(in, in) = out is det, in, in) = out is det.
+:- mode array__foldr(func(in, di) = uo is det, array_ui, di) = uo is det.
+:- mode array__foldr(func(in, di) = uo is det, in, di) = uo is det.
+
+	% array__permutation(A0, A, RS0, RS) permutes the elements in
+	% A0 given random seed RS0 and returns the permuted array in A
+	% and the next random seed in RS.
+	%
+:- pred array__permutation(array(T), array(T), random__supply,
random__supply).
+:- mode array__permutation(array_di, array_uo, mdi, muo) is det.
+
 
%---------------------------------------------------------------------------
--%
 :- implementation.
 
@@ -967,16 +1000,7 @@
 
%---------------------------------------------------------------------------
--%
 
 array__fetch_items(Array, Low, High, List) :-
-        (
-                Low > High
-        ->
-                List = []
-        ;
-                Low1 is Low + 1,
-                array__fetch_items(Array, Low1, High, List0),
-                array__lookup(Array, Low, Item),
-                List = [Item|List0]
-        ).
+	List = foldr_0(func(X, Xs) = [X | Xs], Array, [], Low, High).
 
 
%---------------------------------------------------------------------------
--%
 
@@ -1111,3 +1135,176 @@
 array__elem(Index, Array) = array__lookup(Array, Index).
 
 'array__elem :='(Index, Array, Value) = array__set(Array, Index, Value).
+
+%
----------------------------------------------------------------------------
%
+
+    % array__sort/1 has type specialised versions for arrays of
+    % ints and strings on the expectation that these constitute
+    % the common case and are hence worth providing a fast-path.
+    %
+    % Experiments indicate that type specialisation improves
+    % array__sort/1 by a factor of 30-40%.
+    %
+    % We randomly permute the input array before sorting to
+    % transform the worst case (sorted or mostly sorted input) to
+    % something that will hopefully never turn up in practice.
+    % This is acceptable since it only requires an O(n) pass
+    % over the input array.
+    %
+:- pragma type_spec(array__sort/1, T = int).
+:- pragma type_spec(array__sort/1, T = string).
+
+% array__sort(A) = qsort_subarray(A, array__min(A), array__max(A)).
+
+array__sort(A) = qsort_subarray(P, Lo, Hi) :-
+	Lo = array__min(A),
+	Hi = array__max(A),
+	random__init(2450987, RS),
+	array__permutation(A, P, RS, _).
+
+%
----------------------------------------------------------------------------
%
+
+array__permutation(A0, A, RS0, RS) :-
+	Lo = array__min(A0),
+	Hi = array__max(A0),
+	Sz = array__size(A0),
+	permutation_2(Lo, Lo, Hi, Sz, A0, A, RS0, RS).
+
+
+
+:- pred permutation_2(int, int, int, int, array(T), array(T),
+		random__supply, random__supply).
+:- mode permutation_2(in, in, in, in, array_di, array_uo, mdi, muo) is det.
+
+permutation_2(I, Lo, Hi, Sz, A0, A, RS0, RS) :-
+	( if I > Hi then
+		A  = A0,
+		RS = RS0
+	  else
+	  	random__random(R, RS0, RS1),
+	  	J   = Lo + (R `rem` Sz),
+		Tmp = A0 ^ elem(J),
+		A1  = ((A0
+				^ elem(J) := A0 ^ elem(I))
+				^ elem(I) := Tmp),
+		permutation_2(I + 1, Lo, Hi, Sz, A1, A, RS1, RS)
+	).
+
+%
----------------------------------------------------------------------------
%
+
+    % qsort_subarray(A, Lo, Hi) sorts into ascending order
+    % the elements of array A with indices Lo, Lo + 1, ..., Hi.
+    %
+    % This procedure uses quicksort choosing the pivot as
+    % median of three where candidate pivots have indices
+    % Lo, (Lo + Hi) //2, and Hi.
+    %
+    % NOTE: I tried implementing the partial sort (i.e. into
+    % a totally ordered sequence of unordered subranges of
+    % e.g. 16 elements) and then applying insertion sort in
+    % a single pass, but that was a performance disaster.
+    % Anybody guess why?  This is the method recommended in
+    % Jon Bentley's "Programming Pearls" (Column 10).
+    %
+:- func qsort_subarray(array(T), int, int) = array(T).
+:- mode qsort_subarray(array_di, in, in) = array_uo is det.
+
+:- pragma type_spec(qsort_subarray/3, T = int).
+:- pragma type_spec(qsort_subarray/3, T = string).
+
+qsort_subarray(A0, Lo, Hi) = A :-
+    ( if Lo >= Hi then
+        A     = A0
+      else
+        Mid   = (Lo + Hi) // 2,
+        % Pivot = median_of_three(A0 ^ elem(Lo), A0 ^ elem(Mid), A0 ^
elem(Hi)),
+        Pivot = A0 ^ elem(Mid),
+        partition(Pivot, Lo, Hi, I, A0, A1),
+        A2    = qsort_subarray(A1, Lo, I - 1),
+        A     = qsort_subarray(A2, I + 1, Hi)
+    ).
+
+%
----------------------------------------------------------------------------
%
+
+    % partition(Pivot, Lo, Hi, I, A0, A) reorders the subarray from
+    % Lo to Hi in A0 into A such that all elements less than Pivot come
+    % before all other elements, returning the index of an instance
+    % of Pivot in I.
+    %
+:- pred partition(T, int, int, int, array(T), array(T)).
+:- mode partition(in, in, in, out, array_di, array_uo) is det.
+
+:- pragma type_spec(partition/6, T = int).
+:- pragma type_spec(partition/6, T = string).
+
+partition(Pivot, Lo, Hi, I, A0, A) :-
+    partition_0(Pivot, Lo, Hi, Lo, I, A0, A).
+
+
+
+:- pred partition_0(T, int, int, int, int, array(T), array(T)).
+:- mode partition_0(in, in, in, in, out, array_di, array_uo) is det.
+
+:- pragma type_spec(partition_0/7, T = int).
+:- pragma type_spec(partition_0/7, T = string).
+
+partition_0(Pivot, Lo, Hi, I0, I, A0, A) :-
+    ( if Lo >= Hi then
+        I = I0,
+        A = A0
+      else
+        X = A0 ^ elem(Lo),
+        compare(Result, X, Pivot),
+        (
+            Result = (<),
+            partition_0(Pivot, Lo + 1, Hi, I0, I, A0, A)
+        ;
+            Result = (=),
+            partition_0(Pivot, Lo + 1, Hi, Lo, I, A0, A)
+        ;
+            Result = (>),
+            Y  = A0 ^ elem(Hi),
+            A1 = A0 ^ elem(Hi) := X,
+            A2 = A1 ^ elem(Lo) := Y,
+            partition_0(Pivot, Lo, Hi - 1, I0, I, A2, A)
+        )
+    ).
+
+%
----------------------------------------------------------------------------
%
+
+array__foldl(Fn, A, X) =
+	foldl_0(Fn, A, X, array__min(A), array__max(A)).
+
+
+
+:- func foldl_0(func(T1, T2) = T2, array(T1), T2, int, int) = T2.
+:- mode foldl_0(func(in, in) = out is det, array_ui, in, in, in) = out is
det.
+:- mode foldl_0(func(in, in) = out is det, in, in, in, in) = out is det.
+:- mode foldl_0(func(in, di) = uo is det, array_ui, di, in, in) = uo is
det.
+:- mode foldl_0(func(in, di) = uo is det, in, di, in, in) = uo is det.
+
+foldl_0(Fn, A, X, I, Max) =
+	( if Max < I	then X
+			else foldl_0(Fn, A, Fn(A ^ elem(I), X), I + 1, Max)
+	).
+
+%
----------------------------------------------------------------------------
%
+
+array__foldr(Fn, A, X) =
+	foldr_0(Fn, A, X, array__min(A), array__max(A)).
+
+
+
+:- func foldr_0(func(T1, T2) = T2, array(T1), T2, int, int) = T2.
+:- mode foldr_0(func(in, in) = out is det, array_ui, in, in, in) = out is
det.
+:- mode foldr_0(func(in, in) = out is det, in, in, in, in) = out is det.
+:- mode foldr_0(func(in, di) = uo is det, array_ui, di, in, in) = uo is
det.
+:- mode foldr_0(func(in, di) = uo is det, in, di, in, in) = uo is det.
+
+foldr_0(Fn, A, X, Min, I) =
+	( if I < Min	then X
+			else foldr_0(Fn, A, Fn(A ^ elem(I), X), Min, I - 1)
+	).
+
+%
----------------------------------------------------------------------------
%
+%
----------------------------------------------------------------------------
%

--
Ralph Becket      |      MSR Cambridge      |      rbeck at microsoft.com 

--------------------------------------------------------------------------
mercury-developers mailing list
Post messages to:       mercury-developers at cs.mu.oz.au
Administrative Queries: owner-mercury-developers at cs.mu.oz.au
Subscriptions:          mercury-developers-request at cs.mu.oz.au
--------------------------------------------------------------------------



More information about the developers mailing list