Article View: comp.lang.prolog
Article #1960uniqueness test
From: Heinz-Ulrich Hoppe
Date: Tue, 26 Mar 1991 15:33
Date: Tue, 26 Mar 1991 15:33
125 lines
5008 bytes
5008 bytes
In several of my programs, I use a meta predicate I call u n i q u e.
unique(+Expr,+Goal) is similar to findall/3 or setof/3 in that it
takes a goal as its second argument and an expression, typically
sharing variables with Goal, as its first argument. unique/2 succeeds
and instantiates Expr iff there is exactly one instantiation of Expr
(irrespective of different naming of variables) derivable from any
possible proof of Goal.
Examples: ?- unique(X,member(X,[1,2,3])).
no
?- unique([X,Y],( member(X,[1,2,1]),
member(Y,[2,3,2]),
X + Y =:= 3 ) ).
X = 1
Y = 2
For my applications of unique/2, the following requirements have to
be met:
- The implementation has to allow for nested use (e.g. a goal inside
unique may directly or indirectly involve another call of unique).
- After having found two d i f f e r e n t solutions (in terms of
Expr), the computation has to terminate (with failure). This is not
only for efficiency reasons, but also allows for the processing of
goals with a potentially infinite number of (different) solutions.
In the following, I list two different implementations of unique/2, one
correct and one incorrect. I am interested in any comments on the problem
in general, but specifically in answers to the following questions:
(1) Do you know of any Prolog implementation where the uniqueness test
can be directly performed by a built-in predicate (in accordance with
the computational requirements)?
(2) Do you know of different implementations of a uniqueness test in
Prolog? (Please give ref. or send code!)
(3) Do you have suggestions how to improve the two versions listed?
Ulrich Hoppe
GMD-IPSI
Dolivostr. 15
6100 Darmstadt (FRG)
hoppe@ipsi.darmstadt.gmd.dbp.de
________________________________________________________________________
% Version 1: unique1(+Expr,+Goal)
% Comments:
% (1) A copy of the original goal with fresh variables is
% generated as a reference term for comparison with future
% instantiations which may be pairwise incompatible (in the
% instantiation of variables n o t shared with Expr).
% (2) A marker '$unitest'(Expr,Goal) may be left in the database
% by a previous successful call of Goal.
% db_unify_fact('$unitest'(Expr,CGoal)) is used instead of
% clause('$unitest'(Expr1,CGoal),true) in order to avoid the
% following behavior:
% ?- unique1(X,member(X,[[a,_],[a,1],[a,2]])).
% X = [a,_648]
% ?- unique1(X,member(X,[[a,1],[a,_],[a,2]])).
% no
% Instead, unique1 should fail (and does) in both cases.
% Still, it succeeds on
% ?- unique1(X,member(X,[[a,_],[a,1]])).
% giving X = [a,1] irrespective of the order of elements.
% So, unique specialization of a solution is allowed.
% "Fail" in this case causes a loop over call(Goal).
% (3) If a previous solution exists and cannot be unified
% with the current one, the database is cleared and
% and test_unique1/3 terminates with failure.
% (4) If there is no previous solution, the current solution
% is asserted and "fail" causes the loop.
% Nested use is possible, because the different DB assertions are
% distinguished by the goal. Given an original goal whose execution
% terminates, we can be sure that it cannot appear again as a subgoal
% of itself in the calling hierarchy (otherwise contradiction to non-
% termination).
%
unique1(Expr,Goal) :-
copy(Goal,CGoal), % (1)
!,
test_unique(Expr,Goal,CGoal).
test_unique1(Expr,Goal,CGoal) :-
call(Goal),
( db_unify_fact('$unitest'(Expr,CGoal)), % (2)
fail
; clause('$unitest'(Expr1,CGoal),true), % (3)
Expr1 \= Expr,
!,
retract('$unitest'(Expr1,CGoal)),
fail
; not clause('$unitest'(Expr,CGoal),true), % (4)
asserta('$unitest'(Expr,Goal)),
fail ).
test_unique1(Expr,Goal,CGoal) :-
retract('$unitest'(Expr,Goal)).
db_unify_fact(Fact) :-
retract(Fact),
asserta(Fact).
% Version 2: unique2(+Expr,+Goal)
% Comments:
% This version behaves exactly as unique1/2 if in test_unique1/3
% db_unify_fact(<marker>) were replaced by clause(<marker>,true).
% It does not require DB-interactions (except potentially for copy),
% but is computationally less efficient than version 1 in that the
% first solution of Goal (if there is one) is always calculated twice.
%
unique2(Expr,Goal) :-
copy(p(Expr,Goal),p(CExpr,CGoal)),
call(Goal),
!,
alt_test(Expr,CExpr,CGoal).
test_unique2(Expr,CExpr,CGoal) :-
call(CGoal),
( Expr \= CExpr, !, fail
; fail ).
test_unique2(_,_,_) :- true.
Technical Details
Message-ID:
<1538@ipsi.UUCP>Path:
arc1-ca.pugleaf.net!utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!uunet!mcsun!unido!ipsi!hoppe@ipsi.UUCPShow Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!uunet!mcsun!unido!ipsi!hoppe@ipsi.UUCP From: hoppe@ipsi.UUCP (Heinz-Ulrich Hoppe) Newsgroups: comp.lang.prolog Subject: uniqueness test Message-ID: <1538@ipsi.UUCP> Date: 26 Mar 91 15:33:28 GMT Sender: hoppe@ipsi.UUCP Lines: 125