I need to find the first duplicate value in a list.
prep(3,[1,3,5,3,5]).
Should be true.
prep(5,[1,3,5,3,5]).
Should be false.
I thought checking for equality with the current value and the previous list members until I find a duplicate, if it finds one it will test for equality with X but I have no idea how to do that in Prolog!
I appreciate any help! Thanks
Not sure if this is homework/ there are restrictions on which predicates you are allowed to use, but this gets prolog to do the recursion for you.
It says.. find all duplicates ie. where there is an item with list index I1, and another one with I2, such that they both have the same value N, and the indices are not the same ie.don't consider the same list item as a duplicate.
These duplicates are put (in the order they are found, from the beginning crucially) in list AllDups, and the predicate is true if the first duplicate found unifies with M, the value you are checking.
First Attempt: This works but is very inefficient, building a list of ALL duplicates
Even if you are not allowed to use findall, it might help you work out how to do it 'manually'.
Second Attempt: This doesn't work / backtracks too far yielding a false positive
You can even do it without the findall - use nth0 to find the first duplicate item, then if that unifies with the value you are checking, it returns true, otherwise false.
Third Attempt : This works, and returns as soon as the first duplicate has been found
In this answer we improve the logically pure code presented in this earlier answer. Step-by-step:
We combine two predicates
memberd/2
andnon_member/2
to one,memberd_t/3
, which uses an additional argument for reifying list membership into a truth-value (true
orfalse
).memberd_t/3
is equivalent tomemberd/2
+non_member/2
, so we could define it like this:Or, vice versa, we could define
memberd/2
andnon_member/2
like so:In practise, we use a tuned implementation of
memberd_t/3
—one with better determinism.Let's see that
memberd_t/3
in fact covers bothmemberd/2
andnon_member/2
!Take the following variant of predicate
firstdup/2
(defined earlier) as our starting point:Let's replace the use of
memberd/2
andnon_member/2
withmemberd_t/3
!Let's hoist
memberd_t/3
!Above pattern
pred_t(OtherArgs,T), (T = true -> Then ; T = false, Else)
can be expressed more concisely usingif_/3
, writingif_(pred_t(OtherArgs),Then,Else)
.Let's run some queries!
Here is a pure version using
dif/2
which implements sound inequality.dif/2
is offered by B-Prolog, YAP-Prolog, SICStus-Prolog and SWI-Prolog.The advantages are that it can also be used with more general queries:
Here, we get three answers:
A
is the duplicate in the first two answers, but on two different grounds:A
might be equal toB
orC
. In the third answer,B
is the duplicate, but it will only be a duplicate ifC
is different toA
.To understand the definition, read
:-
as an arrow ← So what is on the right-hand side is what you know and on the left is what you conclude. It is often in the beginning a bit irritating to read predicates in that direction, after all you might be tempted to follow "the thread of execution". But often this thread leads to nowhere – it gets too complex to understand.The first rule reads:
The second rule reads:
The
member/2
predicate is provided in many Prolog systems andnonmember(X,L)
can be defined asmaplist(dif(X),L)
. Thus one would definefirstdup/2
rather as:rep(N,List) :- append(L1,[N|_],List),append(_,[N|_],L1),\+(rep(_,L1)).