[m-rev.] for review 2/3: Implement map.equivalent/2

Julien Fischer jfischer at opturion.com
Sun Jun 30 04:32:37 AEST 2013


Hi Paul,

On Fri, 28 Jun 2013, Paul Bone wrote:

> Implement map.equivalent/2

A better name would be: map.equal/2.
(That name is more consistent with that of similar predicates throughout
the rest of the standard library, for example set_tree234.equal/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.

I suggest:

    Add a new predicate, map.equal/2, that can be used to test whether
    two maps contain the same set of key-value pairs.  Unifying two maps
    will not do this as the standard defintion of equality for 234-trees
    is structural equality.

>
> library/tree234.m:
>    Implement a predicate to test for tree234 equivalence.

You should probably add a similar predicate to the rbtree and assoc_list
modules.  It's useful if all the modules in the standard library that
provide a map ADT provide similar functionality.

>
> NEWS:
>    Announce this change in the news file.

Or just:

      Announce the above changes.

("in the new file" seems a bit redundant, given that the file in
question *is* the NEWS file.)

...

> 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.

Change this to:

    * We have added the predicate map.equal/2, which can be used to
      test whether two maps contain the same set of key-value pairs.
      Similarly, we have added the predicate tree234.equal/2.

...

> 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.

For the last sentence I suggest:

     Use the predicate map.equal/2 if you want to test if two maps
     contain the same set of key-value pairs.

Can you generalise the above  so that the advice in that paragraph is not so
234-tree specific.  (In principle, we should be able to change the
underlying implementation for maps, although in practice I cannot see us
ever doing so.)

> %-----------------------------------------------------------------------------%
> %-----------------------------------------------------------------------------%
>
> @@ -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.
> +    %

I suggest the following:

     % map.equal(MapA, MapB):
     % True iff `MapA' and `MapB' contain the same set of key-value pairs.

> +:- pred map.equivalent(map(K, V)::in, map(K, V)::in) is semidet.
> +
>     % Check whether map contains key
>     %

While you're at it, there's a full stop missing after the word "key"
there.

> :- pred map.contains(map(K, _V)::in, K::in) is semidet.

...

> diff --git a/library/tree234.m b/library/tree234.m
> index 718f6ab..525dbe6 100644
> --- a/library/tree234.m
> +++ b/library/tree234.m

...


> +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

    while doing an inorder traversal of A

> +    % matcking KV pairs.  use two nested loops, the out loop recurses on A,

s/matcking/matching/

s/use/Use/

s/out/outer/

> +    % 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.

Fix your grammar in that last sentence.

> +    %
> +:- 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

That doesn't look right -- shouldn't we fail unless MapB (and the rest
of the stack) is also 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)
> +        )
> +    ).

Please post a revised diff once you have addressed the above comments.

Cheers,
Julien.



More information about the reviews mailing list