[m-rev.] diff: add new higher-order list operations

Julien Fischer juliensf at csse.unimelb.edu.au
Tue Aug 8 14:15:36 AEST 2006


Estimated hours taken: 1
Branches: main

Add some higher-order list predicates requested by Nick Nethercote.

library/list.m:
 	Add list.map2_foldl3/10, list.map_corresponding_foldl/6
 	and list.map_corresponding3_foldl/7.

 	Fix the positioning of some descriptive comments and other
 	minor syntax cleanups.

NEWS:
 	Mention the new predicates.

 	Rearrange this file slightly: the support for embedding Unicode
 	characters in strings should not be in the news for Mercury 0.13.

 	Fix some spelling.

Julien.

Index: NEWS
===================================================================
RCS file: /home/mercury1/repository/mercury/NEWS,v
retrieving revision 1.417
diff -u -r1.417 NEWS
--- NEWS	1 Aug 2006 23:36:55 -0000	1.417
+++ NEWS	8 Aug 2006 04:10:17 -0000
@@ -2,9 +2,29 @@
  --------------------

  Changes to the Mercury language:
+
  * Mutables can now be marked as constant, which is useful when working with
    constant data structures that cannot be conveniently represented as constant
    terms.
+* Unicode characters can now be encoded in string literals using an
+  escape sequence.
+
+Changes to the Mercury standard library:
+
+* We have added list.map2_foldl3/10, list.map_corresponding_foldl/6 and
+  list.map_corresponding3_foldl/7 
+
+DETAILED LISTING
+================
+
+Changes to the Mercury language:
+
+* Unicode characters can now be encoded in string literals using an
+  escape sequence.  The escape sequence \uXXXX (or \UXXXXXXXX), where XXXX
+  (or XXXXXXXX) is a Unicode character code in hexadecimal, is replaced with
+  the corresponding Unicode character.  The encoding used to represent the
+  Unicode character is implementation dependent.  For the Melbourne Mercury
+  compiler, Unicode characters are represented using UTF-8 for the C backends.

  NEWS since Mercury 0.12
  -----------------------
@@ -32,8 +52,6 @@
  * ':' is now the type qualification operator, not a module qualifier.
  * To ensure soundness, goals in negated contexts using non-local variables
    with dynamic modes (inst "any") must now be marked as impure.
-* Unicode characters can now be encoded in string literals using an
-  escape sequence.

  Changes to the Mercury standard library:
  * We have removed the predicates dealing with runtime type information (RTTI)
@@ -64,7 +82,7 @@

  Portability Improvements:
  * We have made the implementation compatible with gcc 4.1.
-* We have ported Mercury to the x86_64 (AMD64 / Intel EMT64) architecure.
+* We have ported Mercury to the x86_64 (AMD64 / Intel EMT64) architecture.

  Changes to the Mercury debugger:
  * Users can now see a listing of the source code lines referred to by the
@@ -78,7 +96,7 @@
  * The source command now accepts optional additional arguments which
    are substituted into the sourced file, allowing for parameterised
    scripts.
-* Users can now open terms in their favorite editor, or
+* Users can now open terms in their favourite editor, or
    perform a grep on a term.
  * The `set' command has been replaced by several other commands: the `format',
    `format_param', `list_context_lines', `list_path', `xml_browser_cmd',
@@ -295,13 +313,6 @@
    sure that the goal is in fact pure, e.g. because they know that
    the goal inside the negation will not instantiate the variable.

-* Unicode characters can now be encoded in string literals using an
-  escape sequence.  The escape sequence \uXXXX (or \UXXXXXXXX), where XXXX
-  (or XXXXXXXX) is a Unicode character code in hexidecimal, is replaced with
-  the corresponding Unicode character.  The encoding used to represent the
-  Unicode character is implementation dependent.  For the Melbourne Mercury
-  compiler, Unicode characters are represented using UTF-8 for the C backends.
-
  Changes to the Mercury standard library:

  * We have added the function `divide_equivalence_classes' to the `eqvclass'
@@ -341,7 +352,7 @@

  Changes to the Mercury debugger:

-* A `list' command has ben added that displays a listing of the source code
+* A `list' command has been added that displays a listing of the source code
    lines referred to by the current environment.  To supporting commands
    `pop_list_dir' and `push_list_dir' have also been added.

@@ -361,7 +372,7 @@
    the corresponding argument of the `source' command.

  * The above two features have been used to create two additional commands: an
-  `open' command that lets users open a term in their favorite editor for 
+  `open' command that lets users open a term in their favourite editor for
    browsing and a `grep' command that can be used to check if a term contains
    a particular pattern.

Index: library/list.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/list.m,v
retrieving revision 1.151
diff -u -r1.151 list.m
--- library/list.m	1 Aug 2006 23:36:57 -0000	1.151
+++ library/list.m	8 Aug 2006 04:07:37 -0000
@@ -493,6 +493,7 @@

      % list.map4(T, L, M1, M2, M3, M4) uses the closure T
      % to transform the elements of L into the elements of M1, M2, M3 and M4.
+    %
  :- pred list.map4(pred(A, B, C, D, E), list(A), list(B), list(C), list(D),
      list(E)).
  :- mode list.map4(pred(in, out, out, out, out) is det, in, out, out, out, out)
@@ -511,6 +512,7 @@
      % list.map5(T, L, M1, M2, M3, M4, M5) uses the closure T
      % to transform the elements of L into the elements of M1, M2, M3, M4
      % and M5.
+    %
  :- pred list.map5(pred(A, B, C, D, E, F), list(A), list(B), list(C), list(D),
      list(E), list(F)).
  :- mode list.map5(pred(in, out, out, out, out, out) is det, in, out, out, out,
@@ -529,6 +531,7 @@
      % list.map6(T, L, M1, M2, M3, M4, M5, M6) uses the closure T
      % to transform the elements of L into the elements of M1, M2, M3, M4,
      % M5 and M6.
+    %
  :- pred list.map6(pred(A, B, C, D, E, F, G), list(A), list(B), list(C),
      list(D), list(E), list(F), list(G)).
  :- mode list.map6(pred(in, out, out, out, out, out, out) is det, in, out, out,
@@ -547,6 +550,7 @@
      % list.map7(T, L, M1, M2, M3, M4, M5, M6, M7) uses the closure T
      % to transform the elements of L into the elements of M1, M2, M3, M4,
      % M5, M6 and M7.
+    %
  :- pred list.map7(pred(A, B, C, D, E, F, G, H), list(A), list(B), list(C),
      list(D), list(E), list(F), list(G), list(H)).
  :- mode list.map7(pred(in, out, out, out, out, out, out, out) is det,
@@ -565,6 +569,7 @@
      % list.map8(T, L, M1, M2, M3, M4, M5, M6, M7) uses the closure T
      % to transform the elements of L into the elements of M1, M2, M3, M4,
      % M5, M6, M7 and M8.
+    %
  :- pred list.map8(pred(A, B, C, D, E, F, G, H, I), list(A), list(B), list(C),
      list(D), list(E), list(F), list(G), list(H), list(I)).
  :- mode list.map8(pred(in, out, out, out, out, out, out, out, out) is det,
@@ -594,7 +599,7 @@
      %
  :- func list.map_corresponding3(func(A, B, C) = D, list(A), list(B), list(C))
      = list(D).
-
+
      % list.filter_map_corresponding/3 is like list.map_corresponding/3
      % except the function argument is semidet and the output list
      % consists of only those applications of the function argument that
@@ -615,6 +620,26 @@
  :- mode list.filter_map_corresponding3(func(in, in, in) = out is semidet,
      in, in, in) = out is det.

+    % list.map_corresponding_foldl/6 is like list.map_corresponding except
+    % that it has an accumulator threaded through it.
+    %
+:- pred list.map_corresponding_foldl(pred(A, B, C, D, D),
+    list(A), list(B), list(C), D, D).
+:- mode list.map_corresponding_foldl(pred(in, in, out, in, out) is det,
+    in, in, out, in, out) is det.
+:- mode list.map_corresponding_foldl(pred(in, in, out, di, uo) is det,
+    in, in, out, di, uo) is det.
+
+    % list.map_corresponding3_foldl7 is like list.map_corresponding3 except
+    % that it has an accumulator threaded through it.
+    %
+:- pred list.map_corresponding3_foldl(pred(A, B, C, D, E, E),
+    list(A), list(B), list(C), list(D), E, E).
+:- mode list.map_corresponding3_foldl(pred(in, in, in, out, in, out) is det,
+    in, in, in, out, in, out) is det.
+:- mode list.map_corresponding3_foldl(pred(in, in, in, out, di, uo) is det,
+    in, in, in, out, di, uo) is det.
+
      % list.foldl(Pred, List, Start, End) calls Pred with each
      % element of List (working left-to-right) and an accumulator
      % (with the initial value of Start), and returns the final
@@ -894,6 +919,30 @@
  :- mode list.map_foldl3(pred(in, out, in, out, in, out, in, out) is nondet,
      in, out, in, out, in, out, in, out) is nondet.

+    % Same as list.map_foldl, but with two mapped outputs and three
+    % accumulators.
+    %
+:- pred list.map2_foldl3(pred(L, M, N, A, A, B, B, C, C),
+    list(L), list(M), list(N), A, A, B, B, C, C).
+:- mode list.map2_foldl3(
+    pred(in, out, out, in, out, in, out, in, out) is det,
+    in, out, out, in, out, in, out, in, out) is det.
+:- mode list.map2_foldl3(
+    pred(in, out, out, in, out, in, out, di, uo) is det,
+    in, out, out, in, out, in, out, di, uo) is det.
+:- mode list.map2_foldl3(
+    pred(in, out, out, in, out, in, out, in, out) is cc_multi,
+    in, out, out, in, out, in, out, in, out) is cc_multi.
+:- mode list.map2_foldl3(
+    pred(in, out, out, in, out, in, out, di, uo) is cc_multi,
+    in, out, out, in, out, in, out, di, uo) is cc_multi.
+:- mode list.map2_foldl3(
+    pred(in, out, out, in, out, in, out, in, out) is semidet,
+    in, out, out, in, out, in, out, in, out) is semidet.
+:- mode list.map2_foldl3(
+    pred(in, out, out, in, out, in, out, in, out) is nondet,
+    in, out, out, in, out, in, out, in, out) is nondet.
+
      % Same as list.map_foldl, but with four accumulators.
      %
  :- pred list.map_foldl4(pred(L, M, A, A, B, B, C, C, D, D), list(L), list(M),
@@ -983,7 +1032,7 @@

      % list.filter(Pred, List, TrueList) takes a closure with one
      % input argument and for each member of List `X', calls the closure.
-    % Iff call(Pred, X) is true, then X is included in TrueList.
+    % Iff Pred(X) is true, then X is included in TrueList.
      %
  :- pred list.filter(pred(X)::in(pred(in) is semidet), list(X)::in,
      list(X)::out) is det.
@@ -992,8 +1041,8 @@

      % list.filter(Pred, List, TrueList, FalseList) takes a closure with one
      % input argument and for each member of List `X', calls the closure.
-    % Iff call(Pred, X) is true, then X is included in TrueList.
-    % Iff call(Pred, X) is false, then X is included in FalseList.
+    % Iff Pred(X) is true, then X is included in TrueList.
+    % Iff Pred(X) is false, then X is included in FalseList.
      %
  :- pred list.filter(pred(X)::in(pred(in) is semidet), list(X)::in,
      list(X)::out, list(X)::out) is det.
@@ -1245,7 +1294,6 @@
  list.index0_of_first_occurrence(List, Elem, N) :-
      list.index0_of_first_occurrence_2(List, Elem, 0, N).

-
  :- pred list.index0_of_first_occurrence_2(list(T)::in, T::in,
      int::in, int::out) is semidet.

@@ -1254,11 +1302,9 @@
              ;  list.index0_of_first_occurrence_2(Xs, Y, N0 + 1, N)
      ).

-
  list.index1_of_first_occurrence(List, Elem, N + 1) :-
      list.index0_of_first_occurrence(List, Elem, N).

-
  list.det_index0_of_first_occurrence(List, Elem) = N :-
      ( list.index0_of_first_occurrence(List, Elem, N0) ->
          N = N0
@@ -1694,46 +1740,46 @@

  list.map(_, [],  []).
  list.map(P, [H0 | T0], [H | T]) :-
-    call(P, H0, H),
+    P(H0, H),
      list.map(P, T0, T).

  list.map2(_, [],  [],  []).
  list.map2(P, [H0 | T0], [H1 | T1], [H2 | T2]) :-
-    call(P, H0, H1, H2),
+    P(H0, H1, H2),
      list.map2(P, T0, T1, T2).

  list.map3(_, [],  [],  [],  []).
  list.map3(P, [H0 | T0], [H1 | T1], [H2 | T2], [H3 | T3]) :-
-    call(P, H0, H1, H2, H3),
+    P(H0, H1, H2, H3),
      list.map3(P, T0, T1, T2, T3).

  list.map4(_, [], [], [], [], []).
  list.map4(P, [H0 | T0], [H1 | T1], [H2 | T2], [H3 | T3], [H4 | T4]) :-
-    call(P, H0, H1, H2, H3, H4),
+    P(H0, H1, H2, H3, H4),
      list.map4(P, T0, T1, T2, T3, T4).

  list.map5(_, [], [], [], [], [], []).
  list.map5(P, [H0 | T0], [H1 | T1], [H2 | T2], [H3 | T3], [H4 | T4], [H5 | T5])
          :-
-    call(P, H0, H1, H2, H3, H4, H5),
+    P(H0, H1, H2, H3, H4, H5),
      list.map5(P, T0, T1, T2, T3, T4, T5).

  list.map6(_, [], [], [], [], [], [], []).
  list.map6(P, [H0 | T0], [H1 | T1], [H2 | T2], [H3 | T3], [H4 | T4], [H5 | T5],
          [H6 | T6]) :-
-    call(P, H0, H1, H2, H3, H4, H5, H6),
+    P(H0, H1, H2, H3, H4, H5, H6),
      list.map6(P, T0, T1, T2, T3, T4, T5, T6).

  list.map7(_, [], [], [], [], [], [], [], []).
  list.map7(P, [H0 | T0], [H1 | T1], [H2 | T2], [H3 | T3], [H4 | T4], [H5 | T5],
          [H6 | T6], [H7 | T7]) :-
-    call(P, H0, H1, H2, H3, H4, H5, H6, H7),
+    P(H0, H1, H2, H3, H4, H5, H6, H7),
      list.map7(P, T0, T1, T2, T3, T4, T5, T6, T7).

  list.map8(_, [], [], [], [], [], [], [], [], []).
  list.map8(P, [H0 | T0], [H1 | T1], [H2 | T2], [H3 | T3], [H4 | T4], [H5 | T5],
          [H6 | T6], [H7 | T7], [H8 | T8]) :-
-    call(P, H0, H1, H2, H3, H4, H5, H6, H7, H8),
+    P(H0, H1, H2, H3, H4, H5, H6, H7, H8),
      list.map8(P, T0, T1, T2, T3, T4, T5, T6, T7, T8).

  list.map_corresponding(_, [], []) = [].
@@ -1798,34 +1844,61 @@
              "mismatched list arguments")
      ).

+list.map_corresponding_foldl(_, [], [], [], !Acc).
+list.map_corresponding_foldl(_, [], [_ | _], _, _, _) :-
+    error("list.map_corresponding_foldl: mismatched list arguments").
+list.map_corresponding_foldl(_, [_ | _], [], _, _, _) :-
+    error("list.map_corresponding_foldl: mismatched list arguments").
+list.map_corresponding_foldl(P, [A | As], [B | Bs], [C | Cs], !Acc) :-
+    P(A, B, C, !Acc),
+    list.map_corresponding_foldl(P, As, Bs, Cs, !Acc).
+
+list.map_corresponding3_foldl(_, [], [], [], [], !Acc).
+list.map_corresponding3_foldl(_, [], [_ | _], [_ | _], _, _, _) :-
+    error("list.map_corresponding3_foldl: mismatched list arguments"). 
+list.map_corresponding3_foldl(_, [_ | _], [], [_ | _], _, _, _) :-
+    error("list.map_corresponding3_foldl: mismatched list arguments"). 
+list.map_corresponding3_foldl(_, [_ | _], [_ | _], [], _, _, _) :-
+    error("list.map_corresponding3_foldl: mismatched list arguments"). 
+list.map_corresponding3_foldl(_, [], [], [_ | _], _, _, _) :-
+    error("list.map_corresponding3_foldl: mismatched list arguments"). 
+list.map_corresponding3_foldl(_, [], [_ | _], [], _, _, _) :-
+    error("list.map_corresponding3_foldl: mismatched list arguments"). 
+list.map_corresponding3_foldl(_, [_ | _], [], [], _, _, _) :-
+    error("list.map_corresponding3_foldl: mismatched list arguments"). 
+list.map_corresponding3_foldl(P, [A | As], [B | Bs], [C | Cs], [D | Ds],
+        !Acc) :-
+    P(A, B, C, D, !Acc),
+    list.map_corresponding3_foldl(P, As, Bs, Cs, Ds, !Acc).
+
  list.foldl(_, [], !A).
  list.foldl(P, [H | T], !A) :-
-    call(P, H, !A),
+    P(H, !A),
      list.foldl(P, T, !A).

  list.foldl2(_, [], !A, !B).
  list.foldl2(P, [H | T], !A, !B) :-
-    call(P, H, !A, !B),
+    P(H, !A, !B),
      list.foldl2(P, T, !A, !B).

  list.foldl3(_, [], !A, !B, !C).
  list.foldl3(P, [H | T], !A, !B, !C) :-
-    call(P, H, !A, !B, !C),
+    P(H, !A, !B, !C),
      list.foldl3(P, T, !A, !B, !C).

  list.foldl4(_, [], !A, !B, !C, !D).
  list.foldl4(P, [H | T], !A, !B, !C, !D) :-
-    call(P, H, !A, !B, !C, !D),
+    P(H, !A, !B, !C, !D),
      list.foldl4(P, T, !A, !B, !C, !D).

  list.foldl5(_, [], !A, !B, !C, !D, !E).
  list.foldl5(P, [H | T], !A, !B, !C, !D, !E) :-
-    call(P, H, !A, !B, !C, !D, !E),
+    P(H, !A, !B, !C, !D, !E),
      list.foldl5(P, T, !A, !B, !C, !D, !E).

  list.foldl6(_, [], !A, !B, !C, !D, !E, !F).
  list.foldl6(P, [H | T], !A, !B, !C, !D, !E, !F) :-
-    call(P, H, !A, !B, !C, !D, !E, !F),
+    P(H, !A, !B, !C, !D, !E, !F),
      list.foldl6(P, T, !A, !B, !C, !D, !E, !F).

  list.foldl_corresponding(_, [], [], !Acc).
@@ -1848,48 +1921,53 @@

  list.map_foldl(_, [], [], !A).
  list.map_foldl(P, [H0 | T0], [H | T], !A) :-
-    call(P, H0, H, !A),
+    P(H0, H, !A),
      list.map_foldl(P, T0, T, !A).

  list.map2_foldl(_, [], [], [], !A).
  list.map2_foldl(P, [H0 | T0], [H1 | T1], [H2 | T2], !A) :-
-    call(P, H0, H1, H2, !A),
+    P(H0, H1, H2, !A),
      list.map2_foldl(P, T0, T1, T2, !A).

  list.map_foldl2(_, [], [], !A, !B).
  list.map_foldl2(P, [H0 | T0], [H | T], !A, !B) :-
-    call(P, H0, H, !A, !B),
+    P(H0, H, !A, !B),
      list.map_foldl2(P, T0, T, !A, !B).

  list.map2_foldl2(_, [], [], [], !A, !B).
  list.map2_foldl2(P, [H0 | T0], [H1 | T1], [H2 | T2], !A, !B) :-
-    call(P, H0, H1, H2, !A, !B),
+    P(H0, H1, H2, !A, !B),
      list.map2_foldl2(P, T0, T1, T2, !A, !B).

  list.map_foldl3(_, [], [], !A, !B, !C).
  list.map_foldl3(P, [H0 | T0], [H | T], !A, !B, !C) :-
-    call(P, H0, H, !A, !B, !C),
+    P(H0, H, !A, !B, !C),
      list.map_foldl3(P, T0, T, !A, !B, !C).

+list.map2_foldl3(_, [], [], [], !A, !B, !C).
+list.map2_foldl3(P, [H0 | T0], [H1 | T1], [H2 | T2], !A, !B, !C) :-
+    P(H0, H1, H2, !A, !B, !C),
+    list.map2_foldl3(P, T0, T1, T2, !A, !B, !C).
+
  list.map_foldl4(_, [], [], !A, !B, !C, !D).
  list.map_foldl4(P, [H0 | T0], [H | T], !A, !B, !C, !D) :-
-    call(P, H0, H, !A, !B, !C, !D),
+    P(H0, H, !A, !B, !C, !D),
      list.map_foldl4(P, T0, T, !A, !B, !C, !D).

  list.map_foldl5(_, [], [], !A, !B, !C, !D, !E).
  list.map_foldl5(P, [H0 | T0], [H | T], !A, !B, !C, !D, !E) :-
-    call(P, H0, H, !A, !B, !C, !D, !E),
+    P(H0, H, !A, !B, !C, !D, !E),
      list.map_foldl5(P, T0, T, !A, !B, !C, !D, !E).

  list.map_foldl6(_, [], [], !A, !B, !C, !D, !E, !F).
  list.map_foldl6(P, [H0 | T0], [H | T], !A, !B, !C, !D, !E, !F) :-
-    call(P, H0, H, !A, !B, !C, !D, !E, !F),
+    P(H0, H, !A, !B, !C, !D, !E, !F),
      list.map_foldl6(P, T0, T, !A, !B, !C, !D, !E, !F).

  list.foldr(_, [], !A).
  list.foldr(P, [H | T], !A) :-
      list.foldr(P, T, !A),
-    call(P, H, !A).
+    P(H, !A).

  list.all_true(_P, []).
  list.all_true(P, [X | Xs]) :-
@@ -1907,7 +1985,7 @@
  list.filter(_, [],  [], []).
  list.filter(P, [H | T], True, False) :-
      list.filter(P, T, TrueTail, FalseTail),
-    ( call(P, H) ->
+    ( P(H) ->
          True = [H | TrueTail],
          False = FalseTail
      ;
@@ -1918,7 +1996,7 @@
  list.filter_map(_, [],  []).
  list.filter_map(P, [H0 | T0], True) :-
      list.filter_map(P, T0, TrueTail),
-    ( call(P, H0, H) ->
+    ( P(H0, H) ->
          True = [H | TrueTail]
      ;
          True = TrueTail
@@ -1927,7 +2005,7 @@
  list.filter_map(_, [], [], []).
  list.filter_map(P, [H0 | T0], True, False) :-
      list.filter_map(P, T0, TrueTail, FalseTail),
-    ( call(P, H0, H) ->
+    ( P(H0, H) ->
          True = [H | TrueTail],
          False = FalseTail
      ;
@@ -1936,14 +2014,14 @@
      ).

  list.find_first_map(P, [X | Xs], A) :-
-    ( call(P, X, A0) ->
+    ( P(X, A0) ->
          A = A0
      ;
          list.find_first_map(P, Xs, A)
      ).

  list.find_first_map2(P, [X | Xs], A, B) :-
-    ( call(P, X, A0, B0) ->
+    ( P(X, A0, B0) ->
          A = A0,
          B = B0
      ;
@@ -1951,7 +2029,7 @@
      ).

  list.find_first_map3(P, [X | Xs], A, B, C) :-
-    ( call(P, X, A0, B0, C0) ->
+    ( P(X, A0, B0, C0) ->
          A = A0,
          B = B0,
          C = C0
@@ -1961,7 +2039,7 @@

  list.takewhile(_, [], [], []).
  list.takewhile(P, [X | Xs], Ins, Outs) :-
-    ( call(P, X) ->
+    ( P(X) ->
          Ins = [X | Ins0],
          list.takewhile(P, Xs, Ins0, Outs)
      ;
@@ -2017,7 +2095,7 @@
          L = [X]
      ; N = 2 ->
          L0 = [X, Y | Rest],
-        call(P, X, Y, C),
+        P(X, Y, C),
          (
              C = (<),
              L = [X, Y]
@@ -2052,7 +2130,7 @@
  list.merge_and_remove_dups(_P, [], [Y | Ys], [Y | Ys]).
  list.merge_and_remove_dups(_P, [X | Xs], [], [X | Xs]).
  list.merge_and_remove_dups(P, [H1 | T1], [H2 | T2], L) :-
-    call(P, H1, H2, C),
+    P(H1, H2, C),
      (
          C = (<),
          L = [H1 | T],

--------------------------------------------------------------------------
mercury-reviews mailing list
Post messages to:       mercury-reviews at csse.unimelb.edu.au
Administrative Queries: owner-mercury-reviews at csse.unimelb.edu.au
Subscriptions:          mercury-reviews-request at csse.unimelb.edu.au
--------------------------------------------------------------------------



More information about the reviews mailing list