[m-rev.] for review 2/3: Implement map.equivalent/2
Paul Bone
paul at bone.id.au
Fri Jun 28 16:26:35 AEST 2013
For review by Julien
Implement map.equivalent/2
The equality test operation on 2-3-4 trees tests if they are structurally
equal. They might also be considered equal if their sets of key-value pairs
are equivalent. I've created an equivalent/2 predicate in the tree234 and
map modules in the standard library to test this.
library/tree234.m:
Implement a predicate to test for tree234 equivalence.
library/map.m:
Forward map.equivalent/2 to tree234.equivalent/2.
NEWS:
Announce this change in the news file.
---
NEWS | 4 +++
library/map.m | 21 +++++++++++++
library/tree234.m | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 116 insertions(+), 1 deletion(-)
diff --git a/NEWS b/NEWS
index d3c6eac..03ed031 100644
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,10 @@ This is a bug-fix release.
for shared libraries and the LD_LIBFLAGS and EXTRA_LD_LIBFLAGS mmake
variables. (Bug #288)
+Changes to the Mercury standard library:
+
+* Equality predicates are now defined for 2-3-4 trees and maps.
+
NEWS for Mercury 13.05.1
------------------------
diff --git a/library/map.m b/library/map.m
index 3eb8d02..c864a23 100644
--- a/library/map.m
+++ b/library/map.m
@@ -19,6 +19,15 @@
% tree234.m. Virtually all the predicates in this file just
% forward the work to the corresponding predicate in tree234.m.
%
+% Note: 2-3-4 trees do not have a canonical representation for any given
+% map. Therefore, two maps with the same key-value pairs may have different
+% internal representations. This means that two maps with the same
+% key-value pairs that were constructed differently may fail to unify or
+% compare as unequal, for example if items are inserted into one of the maps
+% in a different order. This behaviour may change in the future. See
+% map.equivalent/2 below which can be used to test if two maps have the same
+% key-value pairs.
+%
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
@@ -49,6 +58,15 @@
%
:- pred map.is_empty(map(_, _)::in) is semidet.
+ % True if both maps have the same key-value pairs, regardless of how the
+ % maps were constructed.
+ %
+ % Unifying maps does not work as one might expect because the internal
+ % structure of two equivalent maps (according to this predicate) may be
+ % different.
+ %
+:- pred map.equivalent(map(K, V)::in, map(K, V)::in) is semidet.
+
% Check whether map contains key
%
:- pred map.contains(map(K, _V)::in, K::in) is semidet.
@@ -796,6 +814,9 @@ map.singleton(K, V) =
map.is_empty(M) :-
tree234.is_empty(M).
+map.equivalent(MapA, MapB) :-
+ tree234.equivalent(MapA, MapB).
+
map.contains(Map, K) :-
map.search(Map, K, _).
diff --git a/library/tree234.m b/library/tree234.m
index 718f6ab..525dbe6 100644
--- a/library/tree234.m
+++ b/library/tree234.m
@@ -37,6 +37,8 @@
:- pred tree234.is_empty(tree234(K, V)::in) is semidet.
+:- pred tree234.equivalent(tree234(K, V)::in, tree234(K, V)::in) is semidet.
+
%----------------------%
:- pred tree234.member(tree234(K, V)::in, K::out, V::out) is nondet.
@@ -567,6 +569,7 @@
:- import_module int.
:- import_module io.
:- import_module pair.
+:- import_module private.
:- import_module require.
:- import_module set.
:- import_module string.
@@ -607,6 +610,93 @@ tree234.singleton(K, V) = two(K, V, empty, empty).
tree234.is_empty(Tree) :-
Tree = empty.
+tree234.equivalent(MapA, MapB) :-
+ ( pointer_equals(MapA, MapB) ->
+ true
+ ;
+ equivalent(MapA, [MapB], [])
+ ).
+
+ % Use a stack to store the unexplored parts of B while you recurse on A
+ % matcking KV pairs. use two nested loops, the out loop recurses on A,
+ % the inner loop matches from B and the explicit stack.
+ %
+ % Note that B is conceptually the top of B's stack. Allowing us to use
+ % it to make pointer comprisons.
+ %
+:- pred equivalent(tree234(K, V)::in,
+ list(tree234(K, V))::in, list(tree234(K, V))::out) is semidet.
+
+equivalent(MapA, !StackB) :-
+ (
+ !.StackB = [MapB | !:StackB],
+ pointer_equals(MapA, MapB)
+ ->
+ true
+ ;
+ require_complete_switch [MapA]
+ (
+ MapA = empty
+ ;
+ MapA = two(K, V, Left, Right),
+ equivalent(Left, !StackB),
+ match_kv(K, V, !StackB),
+ equivalent(Right, !StackB)
+ ;
+ MapA = three(K1, V1, K2, V2, Left, Middle, Right),
+ equivalent(Left, !StackB),
+ match_kv(K1, V1, !StackB),
+ equivalent(Middle, !StackB),
+ match_kv(K2, V2, !StackB),
+ equivalent(Right, !StackB)
+ ;
+ MapA = four(K1, V1, K2, V2, K3, V3,
+ Left, MidLeft, MidRight, Right),
+ equivalent(Left, !StackB),
+ match_kv(K1, V1, !StackB),
+ equivalent(MidLeft, !StackB),
+ match_kv(K2, V2, !StackB),
+ equivalent(MidRight, !StackB),
+ match_kv(K3, V3, !StackB),
+ equivalent(Right, !StackB)
+ )
+ ).
+
+:- pred match_kv(K::in, V::in,
+ list(tree234(K, V))::in, list(tree234(K, V))::out) is semidet.
+
+match_kv(K, V, !Stack) :-
+ !.Stack = [B | !:Stack],
+ require_complete_switch [B]
+ (
+ B = empty,
+ match_kv(K, V, !Stack)
+ ;
+ B = two(KA, VA, Left, Right),
+ ( Left = empty ->
+ % Try to match the items.
+ K = KA,
+ V = VA,
+ % Update stack and return.
+ !:Stack = [Right | !.Stack]
+ ;
+ % Break up this node and recurse
+ !:Stack = [Left, two(KA, VA, empty, Right) | !.Stack],
+ match_kv(K, V, !Stack)
+ )
+ ;
+ B = three(K1, V1, K2, V2, Left, Middle, Right),
+ !:Stack = [Left, two(K1, V1, empty, Middle), two(K2, V2, empty, Right)
+ | !.Stack],
+ match_kv(K, V, !Stack)
+ ;
+ B = four(K1, V1, K2, V2, K3, V3, Left, MidLeft, MidRight, Right),
+ !:Stack = [Left, two(K1, V1, empty, MidLeft),
+ two(K2, V2, empty, MidRight),
+ two(K3, V3, empty, Right) | !.Stack],
+ match_kv(K, V, !Stack)
+ ).
+
%------------------------------------------------------------------------------%
%------------------------------------------------------------------------------%
@@ -3143,7 +3233,7 @@ tree234.keys_and_values_acc(three(K0, V0, K1, V1, T0, T1, T2),
!:Values = [V0 | !.Values],
tree234.keys_and_values_acc(T0, !Keys, !Values).
tree234.keys_and_values_acc(four(K0, V0, K1, V1, K2, V2, T0, T1, T2, T3),
- !Keys, !Values) :-
+ !Keys, !Values) :-
tree234.keys_and_values_acc(T3, !Keys, !Values),
!:Keys = [K2 | !.Keys],
!:Values = [V2 | !.Values],
--
1.8.1.3
More information about the reviews
mailing list