The following code is based on if_/3 and (=)/3 (aka equal_truth/3 ), as implemented by @false in the Prolog union for AUBUC :
=(X, Y, R) :- X == Y, !, R = true. =(X, Y, R) :- ?=(X, Y), !, R = false. % syntactically different =(X, Y, R) :- X \= Y, !, R = false. % semantically different =(X, Y, R) :- R == true, !, X = Y. =(X, X, true). =(X, Y, false) :- dif(X, Y). if_(C_1, Then_0, Else_0) :- call(C_1, Truth), functor(Truth,_,0), % safety check ( Truth == true -> Then_0 ; Truth == false, Else_0 ).
Compared to occurrences/3 auxiliary occurrences_aux/3 uses a different order of arguments, which passes the list Es as the first argument, which may include indexing the first argument:
occurrences_aux([], _, []). occurrences_aux([X|Xs], E, Ys0) :- if_(E = X, Ys0 = [X|Ys], Ys0 = Ys), occurrences_aux(Xs, E, Ys).
As @migfilg pointed out, the target Fs=[1,2], occurrences_aux(Es,E,Fs) should fail because it is logically false: occurrences_aux(_,E,Fs) indicates that all elements from Fs are equal to E However, occurrences_aux/3 does not end in such cases.
We can use the helper predicate allEqual_to__lazy/2 to improve completion behavior:
allEqual_to__lazy(Xs,E) :- freeze(Xs, allEqual_to__lazy_aux(Xs,E)). allEqual_to__lazy_aux([],_). allEqual_to__lazy_aux([E|Es],E) :- allEqual_to__lazy(Es,E).
With all the auxiliary predicates in place, define occurrences/3 :
occurrences(E,Es,Fs) :- allEqual_to__lazy(Fs,E), % enforce redundant equality constraint lazily occurrences_aux(Es,E,Fs). % flip args to enable first argument indexing
Suppose there are several requests:
?- occurrences(E,Es,Fs). % first, the most general query Es = Fs, Fs = [] ; Es = Fs, Fs = [E] ; Es = Fs, Fs = [E,E] ; Es = Fs, Fs = [E,E,E] ; Es = Fs, Fs = [E,E,E,E] ... % will never terminate universally, but ... % that ok: solution set size is infinite ?- Fs = [1,2], occurrences(E,Es,Fs). false. % terminates thanks to allEqual_to__lazy/2 ?- occurrences(E,[1,2,3,1,2,3,1],Fs). Fs = [1,1,1], E=1 ; Fs = [2,2], E=2 ; Fs = [3,3], E=3 ; Fs = [], dif(E,1), dif(E,2), dif(E,3). ?- occurrences(1,[1,2,3,1,2,3,1],Fs). Fs = [1,1,1]. % succeeds deterministically ?- Es = [E1,E2], occurrences(E,Es,Fs). Es = [E, E], Fs = [E,E], E1=E , E2=E ; Es = [E, E2], Fs = [E], E1=E , dif(E2,E) ; Es = [E1, E], Fs = [E], dif(E1,E), E2=E ; Es = [E1,E2], Fs = [], dif(E1,E), dif(E2,E). ?- occurrences(1,[E1,1,2,1,E2],Fs). E1=1 , E2=1 , Fs = [1,1,1,1] ; E1=1 , dif(E2,1), Fs = [1,1,1] ; dif(E1,1), E2=1 , Fs = [1,1,1] ; dif(E1,1), dif(E2,1), Fs = [1,1].
Edit 2015-04-27
Several requests for testing if the universal termination of occurrences/3 completes in certain cases:
?- occurrences(1,L,[1,2]). false. ?- L = [_|_],occurrences(1,L,[1,2]). false. ?- L = [X|X],occurrences(1,L,[1,2]). false. ?- L = [L|L],occurrences(1,L,[1,2]). false.