I am completely new to Prolog and trying some exercises. One of them is:
Write a predicate set(InList,OutList)
which takes as input an arbitrary
list, and returns a list in which each
element of the input list appears only
once.
Here is my solution:
member(X,[X|_]).
member(X,[_|T]) :- member(X,T).
set([],[]).
set([H|T],[H|Out]) :-
not(member(H,T)),
set(T,Out).
set([H|T],Out) :-
member(H,T),
set(T,Out).
I'm not allowed to use any of built-in predicates (It would be better even do not use not/1). The problem is, that set/2 gives multiple same solutions. The more repetitions in the input list, the more solutions will result. What am I doing wrong? Thanks in advance.
You are getting multiple solutions due to Prolog's backtracking. Technically, each solution provided is correct, which is why it is being generated. If you want just one solution to be generated, you are going to have to stop backtracking at some point. This is what the Prolog cut is used for. You might find that reading up on that will help you with this problem.
Update: Right. Your member() predicate is evaluating as true in several different ways if the first variable is in multiple positions in the second variable.
I've used the name mymember() for this predicate, so as not to conflict with GNU Prolog's builtin member() predicate. My knowledge base now looks like this:
mymember(X,[X|_]).
mymember(X,[_|T]) :- mymember(X,T).
not(A) :- \+ call(A).
set([],[]).
set([H|T],[H|Out]) :-
not(mymember(H,T)),
set(T,Out).
set([H|T],Out) :-
mymember(H,T),
set(T,Out).
So, mymember(1, [1, 1, 1]). evaluates as true in three different ways:
| ?- mymember(1, [1, 1, 1]).
true ? a
true
true
no
If you want to have only one answer, you're going to have to use a cut. Changing the first definition of mymember() to this:
mymember(X,[X|_]) :- !.
Solves your problem.
Furthermore, you can avoid not() altogether, if you wish, by defining a notamember() predicate yourself. The choice is yours.
A simpler (and likely faster) solution is to use library predicate sort/2 which remove duplicates in O(n log n). Definitely works in Yap prolog and SWIPL
You are on the right track... Stay pure---it's easy!
Use reified equality predicates =/3 and dif/3 in combination with if_/3, as implemented in Prolog union for A U B U C:
=(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).
% dif/3 is defined like (=)/3
dif(X, Y, R) :- X == Y, !, R = false.
dif(X, Y, R) :- ?=(X, Y), !, R = true. % syntactically different
dif(X, Y, R) :- X \= Y, !, R = true. % semantically different
dif(X, Y, R) :- R == true, !, X \= Y.
dif(X, Y, true) :- % succeed first!
dif(X, Y).
dif(X, X, false).
if_(C_1, Then_0, Else_0) :-
call(C_1, Truth),
functor(Truth,_,0), % safety check
( Truth == true -> Then_0 ; Truth == false, Else_0 ).
Based on these predicates we build a reified membership predicate list_item_isMember/3. It is semantically equivalent with memberd_truth/3 by #false. We rearrange the argument order so the list is the 1st argument. This enables first-argument indexing which prevents leaving useless choice-points behind as memberd_truth/3 would create.
list_item_isMember([],_,false).
list_item_isMember([X|Xs],E,Truth) :-
if_(E = X, Truth = true, list_item_isMember(Xs,E,Truth)).
list_set([],[]).
list_set([X|Xs],Ys) :-
if_(list_item_isMember(Xs,X), Ys = Ys0, Ys = [X|Ys0]),
list_set(Xs,Ys0).
A simple query shows that all redundant answers have been eliminated and that the goal succeeds without leaving any choice-points behind:
?- list_set([1,2,3,4,1,2,3,4,1,2,3,1,2,1],Xs).
Xs = [4,3,2,1]. % succeeds deterministically
Edit 2015-04-23
I was inspired by #Ludwig's answer of set/2, which goes like this:
set([],[]).
set([H|T],[H|T1]) :- subtract(T,[H],T2), set(T2,T1).
SWI-Prolog's builtin predicate subtract/3 can be non-monotone, which may restrict its use. list_item_subtracted/3 is a monotone variant of it:
list_item_subtracted([],_,[]).
list_item_subtracted([A|As],E,Bs1) :-
if_(dif(A,E), Bs1 = [A|Bs], Bs = Bs1),
list_item_subtracted(As,E,Bs).
list_setB/2 is like set/2, but is based on list_item_subtracted/3---not subtract/3:
list_setB([],[]).
list_setB([X|Xs1],[X|Ys]) :-
list_item_subtracted(Xs1,X,Xs),
list_setB(Xs,Ys).
The following queries compare list_set/2 and list_setB/2:
?- list_set([1,2,3,4,1,2,3,4,1,2,3,1,2,1], Xs).
Xs = [4,3,2,1]. % succeeds deterministically
?- list_setB([1,2,3,4,1,2,3,4,1,2,3,1,2,1],Xs).
Xs = [1,2,3,4]. % succeeds deterministically
?- list_set(Xs,[a,b]).
Xs = [a,b]
; Xs = [a,b,b]
; Xs = [a,b,b,b]
... % does not terminate universally
?- list_setB(Xs,[a,b]).
Xs = [a,b]
; Xs = [a,b,b]
; Xs = [a,b,b,b]
... % does not terminate universally
I think that a better way to do this would be:
set([], []).
set([H|T], [H|T1]) :- subtract(T, [H], T2), set(T2, T1).
So, for example ?- set([1,4,1,1,3,4],S) give you as output:
S = [1, 4, 3]
Adding my answer to this old thread:
notmember(_,[]).
notmember(X,[H|T]):-X\=H,notmember(X,T).
set([],[]).
set([H|T],S):-set(T,S),member(H,S).
set([H|T],[H|S]):-set(T,S),not(member(H,S)).
The only virtue of this solution is that it uses only those predicates that have been introduced by the point where this exercise appears in the original text.
This works without cut, but it needs more lines and another argument.
If I change the [H2|T2] to S on line three, it will produce multiple results. I don't understand why.
setb([],[],_).
setb([H|T],[H|T2],A) :- not(member(H,A)),setb(T,T2,[H|A]).
setb([H|T],[H2|T2],A) :- member(H,A),setb(T,[H2|T2],A).
setb([H|T],[],A) :- member(H,A),setb(T,[],A).
set(L,S) :- setb(L,S,[]).
You just have to stop the backtracking of Prolog.
enter code here
member(X,[X|_]):- !.
member(X,[_|T]) :- member(X,T).
set([],[]).
set([H|T],[H|Out]) :-
not(member(H,T)),
!,
set(T,Out).
set([H|T],Out) :-
member(H,T),
set(T,Out).
Using the support function mymember of Tim, you can do this if the order of elements in the set isn't important:
mymember(X,[X|_]).
mymember(X,[_|T]) :- mymember(X,T).
mkset([],[]).
mkset([T|C], S) :- mymember(T,C),!, mkset(C,S).
mkset([T|C], S) :- mkset(C,Z), S=[T|Z].
So, for example ?- mkset([1,4,1,1,3,4],S) give you as output:
S = [1, 3, 4]
but, if you want a set with the elements ordered like in the list you can use:
mkset2([],[], _).
mkset2([T|C], S, D) :- mkset2(C,Z,[T|D]), ((mymember(T,D), S=Z,!) ; S=[T|Z]).
mkset(L, S) :- mkset2(L,S,[]).
This solution, with the same input of the previous example, give to you:
S = [1, 4, 3]
This time the elements are in the same order as they appear in the input list.
/* Remove duplicates from a list without accumulator */
our_member(A,[A|Rest]).
our_member(A, [_|Rest]):-
our_member(A, Rest).
remove_dup([],[]):-!.
remove_dup([X|Rest],L):-
our_member(X,Rest),!,
remove_dup(Rest,L).
remove_dup([X|Rest],[X|L]):-
remove_dup(Rest,L).
Related
My confusion mainly lies around understanding singleton variables.
I want to implement the predicate noDupl/2 in Prolog. This predicate can be used to identify numbers in a list that appear exactly once, i. e., numbers which are no duplicates. The first argument of noDupl is the list to analyze. The
second argument is the list of numbers which are no duplicates, as described below.
As an example, for the list [2, 0, 3, 2, 1] the result [0, 3, 1] is computed (because 2 is a duplicate).
In my implementation I used the predefined member predicate and used an auxiliary predicate called helper.
I'll explain my logic in pseudocode, so you can help me spot where I went wrong.
First off, If the first element is not a member of the rest of the list, add the first element to the new result List (as it's head).
If the first element is a member of T, call the helper method on the rest of the list, the first element H and the new list.
Helper method, if H is found in the tail, return list without H, i. e., Tail.
noDupl([],[]).
noDupl([H|T],L) :-
\+ member(H,T),
noDupl(T,[H|T]).
noDupl([H|T],L) :-
member(H,T),
helper(T,H,L).
helper([],N,[]).
helper([H|T],H,T). %found place of duplicate & return list without it
helper([H|T],N,L) :-
helper(T,N,[H|T1]).%still couldn't locate the place, so add H to the new List as it's not a duplicate
While I'm writing my code, I'm always having trouble with deciding to choose a new variable or use the one defined in the predicate arguments when it comes to free variables specifically.
Thanks.
Warnings about singleton variables are not the actual problem.
Singleton variables are logical variables that occur once in some Prolog clause (fact or rule). Prolog warns you about these variables if they are named like non-singleton variables, i. e., if their name does not start with a _.
This convention helps avoid typos of the nasty kind—typos which do not cause syntax errors but do change the meaning.
Let's build a canonical solution to your problem.
First, forget about CamelCase and pick a proper predicate name that reflects the relational nature of the problem at hand: how about list_uniques/2?
Then, document cases in which you expect the predicate to give one answer, multiple answers or no answer at all. How?
Not as mere text, but as queries.
Start with the most general query:
?- list_uniques(Xs, Ys).
Add some ground queries:
?- list_uniques([], []).
?- list_uniques([1,2,2,1,3,4], [3,4]).
?- list_uniques([a,b,b,a], []).
And add queries containing variables:
?- list_uniques([n,i,x,o,n], Xs).
?- list_uniques([i,s,p,y,i,s,p,y], Xs).
?- list_uniques([A,B], [X,Y]).
?- list_uniques([A,B,C], [D,E]).
?- list_uniques([A,B,C,D], [X]).
Now let's write some code! Based on library(reif) write:
:- use_module(library(reif)).
list_uniques(Xs, Ys) :-
list_past_uniques(Xs, [], Ys).
list_past_uniques([], _, []). % auxiliary predicate
list_past_uniques([X|Xs], Xs0, Ys) :-
if_((memberd_t(X,Xs) ; memberd_t(X,Xs0)),
Ys = Ys0,
Ys = [X|Ys0]),
list_past_uniques(Xs, [X|Xs0], Ys0).
What's going on?
list_uniques/2 is built upon the helper predicate list_past_uniques/3
At any point, list_past_uniques/3 keeps track of:
all items ahead (Xs) and
all items "behind" (Xs0) some item of the original list X.
If X is a member of either list, then Ys skips X—it's not unique!
Otherwise, X is unique and it occurs in Ys (as its list head).
Let's run some of the above queries using SWI-Prolog 8.0.0:
?- list_uniques(Xs, Ys).
Xs = [], Ys = []
; Xs = [_A], Ys = [_A]
; Xs = [_A,_A], Ys = []
; Xs = [_A,_A,_A], Ys = []
...
?- list_uniques([], []).
true.
?- list_uniques([1,2,2,1,3,4], [3,4]).
true.
?- list_uniques([a,b,b,a], []).
true.
?- list_uniques([1,2,2,1,3,4], Xs).
Xs = [3,4].
?- list_uniques([n,i,x,o,n], Xs).
Xs = [i,x,o].
?- list_uniques([i,s,p,y,i,s,p,y], Xs).
Xs = [].
?- list_uniques([A,B], [X,Y]).
A = X, B = Y, dif(Y,X).
?- list_uniques([A,B,C], [D,E]).
false.
?- list_uniques([A,B,C,D], [X]).
A = B, B = C, D = X, dif(X,C)
; A = B, B = D, C = X, dif(X,D)
; A = C, C = D, B = X, dif(D,X)
; A = X, B = C, C = D, dif(D,X)
; false.
Just like my previous answer, the following answer is based on library(reif)—and uses it in a somewhat more idiomatic way.
:- use_module(library(reif)).
list_uniques([], []).
list_uniques([V|Vs], Xs) :-
tpartition(=(V), Vs, Equals, Difs),
if_(Equals = [], Xs = [V|Xs0], Xs = Xs0),
list_uniques(Difs, Xs0).
While this code does not improve upon my previous one regarding efficiency / complexity, it is arguably more readable (fewer arguments in the recursion).
In this solution a slightly modified version of tpartition is used to have more control over what happens when an item passes the condition (or not):
tpartition_p(P_2, OnTrue_5, OnFalse_5, OnEnd_4, InitialTrue, InitialFalse, Xs, RTrue, RFalse) :-
i_tpartition_p(Xs, P_2, OnTrue_5, OnFalse_5, OnEnd_4, InitialTrue, InitialFalse, RTrue, RFalse).
i_tpartition_p([], _P_2, _OnTrue_5, _OnFalse_5, OnEnd_4, CurrentTrue, CurrentFalse, RTrue, RFalse):-
call(OnEnd_4, CurrentTrue, CurrentFalse, RTrue, RFalse).
i_tpartition_p([X|Xs], P_2, OnTrue_5, OnFalse_5, OnEnd_4, CurrentTrue, CurrentFalse, RTrue, RFalse):-
if_( call(P_2, X)
, call(OnTrue_5, X, CurrentTrue, CurrentFalse, NCurrentTrue, NCurrentFalse)
, call(OnFalse_5, X, CurrentTrue, CurrentFalse, NCurrentTrue, NCurrentFalse) ),
i_tpartition_p(Xs, P_2, OnTrue_5, OnFalse_5, OnEnd_4, NCurrentTrue, NCurrentFalse, RTrue, RFalse).
InitialTrue/InitialFalse and RTrue/RFalse contains the desired initial and final state, procedures OnTrue_5 and OnFalse_5 manage state transition after testing the condition P_2 on each item and OnEnd_4 manages the last transition.
With the following code for list_uniques/2:
list_uniques([], []).
list_uniques([V|Vs], Xs) :-
tpartition_p(=(V), on_true, on_false, on_end, false, Difs, Vs, HasDuplicates, []),
if_(=(HasDuplicates), Xs=Xs0, Xs = [V|Xs0]),
list_uniques(Difs, Xs0).
on_true(_, _, Difs, true, Difs).
on_false(X, HasDuplicates, [X|Xs], HasDuplicates, Xs).
on_end(HasDuplicates, Difs, HasDuplicates, Difs).
When the item passes the filter (its a duplicate) we just mark that the list has duplicates and skip the item, otherwise the item is kept for further processing.
This answer goes similar ways as this previous answer by #gusbro.
However, it does not propose a somewhat baroque version of tpartition/4, but instead an augmented, but hopefully leaner, version of tfilter/3 called tfilter_t/4 which can be defined like so:
tfilter_t(C_2, Es, Fs, T) :-
i_tfilter_t(Es, C_2, Fs, T).
i_tfilter_t([], _, [], true).
i_tfilter_t([E|Es], C_2, Fs0, T) :-
if_(call(C_2,E),
( Fs0 = [E|Fs], i_tfilter_t(Es,C_2,Fs,T) ),
( Fs0 = Fs, T = false, tfilter(C_2,Es,Fs) )).
Adapting list_uniques/2 is straightforward:
list_uniques([], []).
list_uniques([V|Vs], Xs) :-
if_(tfilter_t(dif(V),Vs,Difs), Xs = [V|Xs0], Xs = Xs0),
list_uniques(Difs, Xs0).
Save scrollbars. Stay lean! Use filter_t/4.
You have problems already in the first predicate, noDupl/2.
The first clause, noDupl([], []). looks fine.
The second clause is wrong.
noDupl([H|T],L):-
\+member(H,T),
noDupl(T,[H|T]).
What does that really mean I leave as an exercise to you. If you want, however, to add H to the result, you would write it like this:
noDupl([H|T], [H|L]) :-
\+ member(H, T),
noDupl(T, L).
Please look carefully at this and try to understand. The H is added to the result by unifying the result (the second argument in the head) to a list with H as the head and the variable L as the tail. The singleton variable L in your definition is a singleton because there is a mistake in your definition, namely, you do nothing at all with it.
The last clause has a different kind of problem. You try to clean the rest of the list from this one element, but you never return to the original task of getting rid of all duplicates. It could be fixed like this:
noDupl([H|T], L) :-
member(H, T),
helper(T, H, T0),
noDupl(T0, L).
Your helper/3 cleans the rest of the original list from the duplicate, unifying the result with T0, then uses this clean list to continue removing duplicates.
Now on to your helper. The first clause seems fine but has a singleton variable. This is a valid case where you don't want to do anything with this argument, so you "declare" it unused for example like this:
helper([], _, []).
The second clause is problematic because it removes a single occurrence. What should happen if you call:
?- helper([1,2,3,2], 2, L).
The last clause also has a problem. Just because you use different names for two variables, this doesn't make them different. To fix these two clauses, you can for example do:
helper([H|T], H, L) :-
helper(T, H, L).
helper([H|T], X, [H|L]) :-
dif(H, X),
helper(T, X, L).
These are the minimal corrections that will give you an answer when the first argument of noDupl/2 is ground. You could do this check this by renaming noDupl/2 to noDupl_ground/2 and defining noDupl/2 as:
noDupl(L, R) :-
must_be(ground, L),
noDupl_ground(L, R).
Try to see what you get for different queries with the current naive implementation and ask if you have further questions. It is still full of problems, but it really depends on how you will use it and what you want out of the answer.
I'm trying to create a rule F(C,L) where C and L are integer lists. L contains the index number (starting from 1) of all the elements of C that are equal to 43. My code is shown below. When I try F([43,42,43,42,42,43],L). it returns true. What have I done wrong? Thanks in advance!
F(C,L) :-
forall(
(
member(X,C),
X=43,
nth1(N,C,X)
),
member(N,L)
).
The code by #CapelliC works, but only when used with sufficient instantiation.
?- f([43,42,43,42,42,43], Ps).
Ps = [1,3,6]. % ok
?- f([A,B], Ps).
Ps = [1,2]. % BAD
?- f(_, _).
**LOOPS** % BAD: doesn't terminate
To safeguard against problems like these we can use
iwhen/2 like so:
f_safe(C, L) :-
iwhen(ground(C), findall(X,nth1(X,C,43),L)).
Let's re-run above queries with SWI-Prolog:
?- f_safe([43,42,43,42,42,43], Ps).
Ps = [1,3,6]. % still ok
?- f_safe([A,B], Ps). % BETTER
ERROR: Arguments are not sufficiently instantiated
?- f_safe(_, _). % BETTER
ERROR: Arguments are not sufficiently instantiated
Take it step by step:
:- use_module(library(clpfd)).
list_contains_at1s(Elements, Member, Positions) :-
list_contains_at_index1(Elements, Member, Positions, 1).
list_contains_at_index1([], _, [], _).
list_contains_at_index1([E|Es], E, [I1|Is], I1) :-
I2 #= I1+1,
list_contains_at_index1(Es, E, Is, I2).
list_contains_at_index1([E|Es], X, Is, I1) :-
dif(X, E),
I2 #= I1+1,
list_contains_at_index1(Es, X, Is, I2).
Sample query with SWI-Prolog:
?- list_contains_at1s([43,42,43,42,42,43], 43, Positions).
Positions = [1,3,6]
; false. % left-over choicepoint
Syntax error apart, you're doing it more complex than needed. Keep it simpler, and use findall/3 instead of forall/2. The latter cannot be used to instantiate variables outside its scope.
f(C,L) :- findall(X, nth1(X,C,43), L).
While this previous answer preserves logical-purity, it shows some inefficiency in queries like:
?- list_contains_at1s([43,42,43,42,42,43], 43, Ps).
Ps = [1,3,6] ; % <------ SWI toplevel indicates lingering choicepoint
false.
In above query the lingering choicepoint is guaranteed to be useless: we know that above use case can never yield more than one solution.
Method 1: explicit indexing and extra helper predicate
The earlier definition of list_contains_at_index1/4 has two recursive clauses—one covering the "equal" case, the other one covering the "not equal" case.
Note that these two recursive clauses of list_contains_at_index1/4 are mutually exclusive, because (=)/2 and dif/2 are mutually exclusive.
How can we exploit this?
By utilizing first-argument indexing together with the reified term equality predicate (=)/3!
:- use_module(library(reif)).
list_contains_at_index1([], _, [], _).
list_contains_at_index1([E|Es], X, Is0, I1) :-
=(E, X, T), % (=)/3
equal_here_at0_at(T, I1, Is0, Is),
I2 #= I1+1,
list_contains_at_index1(Es, X, Is, I2).
equal_here_at0_at(true , I1, [I1|Is], Is). % index on the truth value ...
equal_here_at0_at(false, _, Is , Is). % ... of reified term equality
Method 2: implicit indexing, no extra helper predicate, using if_/3
For more concise code we can put if_/3 to good use:
list_contains_at_index1([], _, [], _).
list_contains_at_index1([E|Es], X, Is0, I1) :-
if_(E = X, Is0 = [I1|Is], Is0 = Is),
I2 #= I1+1,
list_contains_at_index1(Es, X, Is, I2).
If we re-run above query with new improved code ...
?- list_contains_at1s([43,42,43,42,42,43], 43, Positions).
Positions = [1, 3, 6].
... we see that the query now succeeds deterministically. Mission accomplished!
My aim is writing a predicate filter/3. With input list [bar(a,12),bar(b,12),bar(c,13)] and filter criteria bar(A,12) the expected output is [bar(a,12),bar(b,12)].
The code below works but what is the difference between writing \+ \+ Filter = X and Filter = X (for me it is same). I wrote down the program by using 2 versions and it gave the same correct result. But I am sure that they are different?!
filter([],_,[]).
filter([X|XS],Filter,[X|ZS]) :-
\+ \+ Filter=X,
!,
filter(XS,Filter,ZS).
filter([_|XS],Filter,ZS) :-
filter(XS,Filter,ZS).
EDIT:
#lurker you are right, they do not give the same result. ( it was my mistake)
----using \+ \+ Filter = X -----
?- filter([foo(a,12),foo(c,12),foo(b,13)],foo(A,12),Res).
Res = [foo(a, 12), foo(c, 12)].
----using Filter = X -----
?- filter([foo(a,12),foo(c,12),foo(b,13)],foo(A,12),Res).
A = a,
Res = [foo(a, 12)].
?- filter([foo(a,12),foo(a,12),foo(b,13)],foo(A,12),Res).
A = a,
Res = [foo(a, 12), foo(a, 12)].
TL;DR
?- tfilter(\bar(_,S)^(S=12), Xs, Ys).
Now, step-by-step:
There are several issues with your program. The biggest is the actual problem statement which leaves several things open. For example, I assume that you expect that all elements are of the form bar(X, N) and you want to select those with N = 12. What you have implemented is slightly different:
?- filter([bar(a,12),bar(b,12),bar(c,13)], bar(_,12), []).
true.
This anomaly is due to your specific use of the cut. As you can see from the other answers, many versions avoid it. Cut is extremely difficult to use without any surprising effects. #CapelliC's version with cut actually avoids this one problem, but this is a very tricky business.
A further anomaly concerns the way how you might want to generalize your query. What about asking:
?- filter([X], bar(_,12), Xs).
What should a correct answer be? Should Xs include X or not? After all, instances of this query produce different results, too! I will show two of them by adding the goals X = bar(a,12) and X = bar(a,13) in front.
?- X = bar(a,12), filter([X], bar(_,12), Xs).
Xs = [bar(a,12)].
?- X = bar(a,13), filter([X], bar(_,12), Xs).
Xs = [].
So in one case we have an element, and in the other we have not. The general query should thus consequently produce two answers.
Here is an approach which does not have such problems:
State the positive selection criteria.
Let's use a separate predicate for the selection criteria, and call it _true:
snd_bar_true(N, bar(_,N)).
State the negative selection criteria.
snd_bar_false(N, bar(_,S)) :-
dif(N, S).
Now, with both, we can write a clean and correct filter program. Note that N is now just the second argument.
filter([], _N, []).
filter([X|Xs], N, [X|Ys]) :-
snd_bar_true(N, X),
filter(Xs, N, Ys).
filter([X|Xs], N, Ys) :-
snd_bar_false(N, X),
filter(Xs, N, Ys).
?- filter([X], 12, Xs).
X = bar(_A, 12), Xs = [bar(_A, 12)]
; X = bar(_A, _B), Xs = [], dif(_B, 12).
So we get two answers: One selecting the element X provided it is of the form bar(_,12). And the other one, which does not select the element, but ensures that the second element is not 12.
While these answers are all perfect and fine, I'm not very happy with it: It is correct but soo verbose. Here is a way to make it more compact.
Merge the criteria into one "reified" definition
snd_bar_t(N, bar(_,N), true).
snd_bar_t(N, bar(_,S), false) :-
dif(S,N).
There is a more compact and efficient way to express this using (=)/3
snd_bar_t(N, bar(_,S), T) :-
=(S, N, T).
=(X, X, true).
=(X, Y, false) :-
dif(X,Y).
This (=)/3 can be more efficiently implemented as:
=(X, Y, T) :-
( X == Y -> T = true
; X \= Y -> T = false
; T = true, X = Y
; T = false,
dif(X, Y)
).
Now, we can use the generic tfilter/3:
filter(Xs, N, Ys) :-
tfilter(snd_bar_t(N), Xs, Ys).
And then, we can use library(lambda) to avoid the auxiliary definition:
filter(Xs, N, Ys) :-
tfilter(N+\bar(_,S)^(S = N), Xs, Ys).
Note that this (S = N) is not what you probably think! It is effectively not simple equality, but actually, the reified version of it! So it will be called like: call((S = 12), T) and thus =(S, 12, T).
Double negation it's an old 'trick of the trade' often used while writing metainterpreters.
Since variables instantiation due to unification it's undone on backtracking, it has a procedural only semantic of "prove a goal without binding its variables", whatever the meaning of such phrase could be.
1 ?- filter([bar(a,12),bar(b,12),bar(c,13)],bar(_,12),L).
L = [bar(a, 12), bar(b, 12)].
If you comment out (i.e. remove) the double negation, you observe the undue instantiation effect: X has been bound to bar(a,12), and then cannot be matched to bar(b,12).
2 ?- filter([bar(a,12),bar(b,12),bar(c,13)],bar(_,12),L).
L = [bar(a, 12)].
edit for the simple case at hand, an alternative implementation of filter/3 could be
filter([],_,[]).
filter([X|XS],Filter,ZS):-
X \= Filter, !, filter(XS, Filter, ZS).
filter([X|XS],Filter,[X|ZS]):-
filter(XS, Filter, ZS).
or, better
filter([],_,[]).
filter([X|XS],Filter,R):-
(X \= Filter -> R = ZS ; R = [X|ZS]), filter(XS, Filter, ZS).
but if your system implements subsumes_term/2, #Boris' answer is to be preferred
The answer by #CapelliC answers your question.
There is another standard predicate, subsumes_term/2, which can be used to achieve the same effect as the double negation:
filter0([], _, []).
filter0([X|Xs], T, Ys) :-
\+ subsumes_term(T, X),
filter0(Xs, T, Ys).
filter0([X|Xs], T, [X|Ys]) :-
subsumes_term(T, X),
filter0(Xs, T, Ys).
As to how to do the iteration over all elements, instead of a cut, prefer a conditional:
filter1([], _, []).
filter1([X|Xs], T, R) :-
( subsumes_term(T, X)
-> R = [X|Ys]
; R = Ys
),
filter1(Xs, T, Ys).
And if you write this, you can as well use include/3 (which, by the way, is literally a "filter" predicate):
filter(List, Term, Filtered) :-
include(subsumes_term(Term), List, Filtered).
I've started to learn Prolog recently and I can't solve how to make union of three lists.
I was able to make union of 2 lists :
%element
element(X,[X|_]).
element(X,[_|Y]):-
element(X,Y).
%union
union([],M,M).
union([X|Y],L,S) :- element(X,L),union(Y,L,S).
union([X|Y],L,[X|S]) :- (not(element(X,L))),union(Y,L,S).
can anybody help me please ?
union(A, B, C, U) :-
union(A, B, V),
union(C, V, U).
Your definition of union/3 can be improved by replacing
... not(element(X,L)), ...
by
... maplist(dif(X),L), ...
or
... non_member(X, L), ....
non_member(_X, []).
non_member(X, [E|Es]) :-
dif(X, E),
non_member(X, Es).
Here is a case where the difference shows:
?- union([A],[B],[C,D]).
A = C, B = D, dif(C, D).
How must [A] and [B] look like such that their union contains 2 elements?
The answer is: they must be different.
Your original version fails for this query, yet, it succeeds for a specialized instance like:
?- A = 1, B = 2, union([A],[B],[C,D]).
So it succeeds for this, but fails for a generalization of it. Therefore it is not a pure, logical relation.
So is everything fine and perfect with dif/2? Unfortunately not. #TudorBerariu has good reason to go for a cut, since it reflects some of the intention we have about the relation. The cut effectively reflects two key intentions
that the alternative of not being a member is now excluded, which is true for certain modes, like Arg1 and Arg2 being both sufficiently instantiated terms. A safe approximation would be ground terms.
that there is no need to look at further elements in the list Arg2, which again is only true if Arg1 and Arg2 are sufficiently instantiated.
Problems only show when terms are not sufficiently instantiated..
The drawback of OP's definition and the one above, is that both are unnecessarily too general which can be observed with repeated elements in Arg2:
?- union([a,a],[a,a],Zs).
Zs = [a, a]
; Zs = [a, a]
; Zs = [a, a]
; Zs = [a, a]
; false.
In fact, we get |Arg2||Arg1|-1 redundant answers. So the cut had some good reason to be there.
Another reason why union/3 as it stands is not very efficient is that for the (intended) ground case it leaves open unnecessary choice points. Again, #TudorBerariu's solution does not have this problem:
?- union([a],[a],Zs).
Zs = [a]
; false. % <--- Prolog does not know that there is nothing left
Eliminating redundancy
The actual culprit for that many redundant answers is the first rule. element(a,[a,a]) (commonly called member/2) will succeed twice.
union([X|Y],L,S) :- element(X,L), union(Y,L,S).
^^^^^^^^^^^^
Here is an improved definition:
memberd(X, [X|_Ys]).
memberd(X, [Y|Ys]) :-
dif(X,Y), % new!
memberd(X, Ys).
The recursive rule, reading it right-to-left, reads as follows:
Assume memberd(X, Ys) is true already for some X and Ys. Given that, and given that we have a fitting Y which is different from X. Thenwe can conclude that also memberd(X, [Y|Ys]) is true.
So this has eliminated the redundant solutions. But our definition is still not very efficient: it still has to visit Arg2 twice for each element, and then it is unable to conclude that no alternatives are left. In any case: resist to place a cut to remove this.
Introducing determinism via reification.
Compare the definitions of memberd/2 and non_member/2. Although they describe "the opposite" of each other, they look very similar:
non_member(_X, []).
non_member(X, [Y|Ys]) :-
dif(X,Y),
non_member(X, Ys).
memberd(X, [X|_Ys]).
memberd(X, [Y|Ys]) :-
dif(X,Y),
memberd(X, Ys).
The recursive rule is the same! Only the fact is a different one. Let's merge them into one definition - with an additional argument telling whether we mean memberd (true) or non_member (false):
memberd_t(_X, [], false).
memberd_t(X, [X|_Ys], true).
memberd_t(X, [Y|Ys], Truth) :-
dif(X, Y),
memberd_t(X, Ys, Truth).
Now, our definition gets a bit more compact:
unionp([], Ys, Ys).
unionp([X|Xs], Ys, Zs0) :-
if_( memberd_t(X, Ys), Zs0 = Zs, Zs0 = [X|Zs] ),
unionp(Xs, Ys, Zs).
memberd_t(_X, [], false). % see below
memberd_t(X, [Y|Ys], Truth) :-
if_( X = Y, Truth=true, memberd_t(X, Ys, Truth) ).
Note the difference between if_(If_1, Then_0, Else_0) and the if-then-else control construct ( If_0 -> Then_0 ; Else_0 ). While If_1 may succeed several times with different truth values (that is, it can be both true and false), the control construct makes If_0 succeed only once for being true only.
if_(If_1, Then_0, Else_0) :-
call(If_1, T),
( T == true -> call(Then_0)
; T == false -> call(Else_0)
; nonvar(T) -> throw(error(type_error(boolean,T),_))
; /* var(T) */ throw(error(instantiation_error,_))
).
=(X, Y, T) :-
( X == Y -> T = true
; X \= Y -> T = false
; T = true, X = Y
; T = false,
dif(X, Y) % ISO extension
% throw(error(instantiation_error,_)) % ISO strict
).
equal_t(X, Y, T) :-
=(X, Y, T).
To ensure that memberd_t/3 will always profit from first-argument indexing, rather use the following definition (thanks to #WillNess):
memberd_t(E, Xs, T) :-
i_memberd_t(Xs, E, T).
i_memberd_t([], _E, false).
i_memberd_t([X|Xs], E, T) :-
if_( X = E, T = true, i_memberd_t(Xs, E, T) ).
You can make the union of the first two lists and then the union between that result and the third:
union(L1, L2, L3, U):-union(L1, L2, U12), union(U12, L3, U).
You can improve union/3 with a cut operator:
union([],M,M).
union([X|Y],L,S) :- element(X,L), !, union(Y,L,S).
union([X|Y],L,[X|S]) :- union(Y,L,S).
Using only predicates with an extra argument such as memberd_t/3 leads only to weak reification. For strong reification we also need to generate constraints. Strong reification is a further approach to eliminate non-determinism.
But strong reification is difficult, a possible way to archive this is to use a CLP(*) instance which has also reified logical operators. Here is an example if using CLP(FD) for the union problem. Unfortunately this covers only the domain Z:
Strong Reification Code:
member(_, [], 0).
member(X, [Y|Z], B) :-
(X #= Y) #\/ C #<==> B,
member(X, Z, C).
union([], X, X).
union([X|Y], Z, T) :-
freeze(B, (B==1 -> T=R; T=[X|R])),
member(X, Z, B),
union(Y, Z, R).
The above doesn't suffer from unnecessary choice points. Here are some example that show that this isn't happening anymore:
Running a Ground Example:
?- union([1,2],[2,3],X).
X = [1, 2, 3].
Also the above example even doesn't create choice points, if we use variables somewhere. But we might see a lot of constraints:
Running a Non-Ground Example:
?- union([1,X],[X,3],Y).
X#=3#<==>_G316,
1#=X#<==>_G322,
_G316 in 0..1,
freeze(_G322, (_G322==1->Y=[X, 3];Y=[1, X, 3])),
_G322 in 0..1.
?- union([1,X],[X,3],Y), X=2.
X = 2,
Y = [1, 2, 3].
Since we didn't formulate some input invariants, the interpreter isn't able to see that producing constraints in the above case doesn't make any sense. We can use the all_different/1 constraint to help the interpreter a little bit:
Providing Invariants:
?- all_different([1,X]), all_different([X,3]), union([1,X],[X,3],Y).
Y = [1, X, 3],
X in inf..0\/2\/4..sup,
all_different([X, 3]),
all_different([1, X]).
But we shouldn't expect too much from this singular example. Since the CLP(FD) and the freeze/2 is only an incomplete decision procedure for propositions and Z equations, the approach might not work as smooth as here in every situation.
Bye
I have a strange problem that I do not know how to solve.
I have written a predicate that compresses lists by removing repeating items.
So if the input is [a,a,a,a,b,c,c,a,a], output should be [a,b,c,a]. My first code worked, but the item order was wrong. So I add a append/3 goal and it stopped working altogether.
Can't figure out why. I tried to trace and debug but don't know what is wrong.
Here is my code which works but gets the item order wrong:
p08([Z], X, [Z|X]).
p08([H1,H2|T], O, X) :-
H1 \= H2,
p08([H2|T], [H1|O], X).
p08([H1,H1|T], O, X) :-
p08([H1|T], O, X).
Here's the newer version, but it does not work at all:
p08([Z], X, [Z|X]).
p08([H1,H2|T], O, X) :-
H1 \= H2,
append(H1, O, N),
p08([H2|T], N, X).
p08([H1,H1|T], O, X) :-
p08([H1|T], O, X).
H1 is not a list, that's why append(H1, O, N) fails.
And if you change H1 to [H1] you actually get a solution identical to your first one. In order to really reverse the list in the accumulator you should change the order of the first two arguments: append(O, [H1], N). Also, you should change the first rule with one that matches the empty list p08([], X, X) (without it, the goal p08([], [], Out) fails).
Now, to solve your problem, here is the simplest solution (which is already tail recursive, as #false stated in the comments to this answer, so there is no need for an accumulator)
p([], []). % Rule for empty list
p([Head, Head|Rest], Out):- % Ignore the Head if it unifies with the 2nd element
!,
p([Head|Rest], Out).
p([Head|Tail], [Head|Out]):- % otherwise, Head must be part of the second list
p(Tail, Out).
and if you want one similar to yours (using an accumulator):
p08(List, Out):-p08(List, [], Out).
p08([], Acc, Acc).
p08([Head, Head|Rest], Acc, Out):-
!,
p08([Head|Rest], Acc, Out).
p08([Head|Tail], Acc, Out):-
append(Acc, [Head], Acc2),
p08(Tail, Acc2, Out).
Pure and simple:
list_withoutAdjacentDuplicates([],[]).
list_withoutAdjacentDuplicates([X],[X]).
list_withoutAdjacentDuplicates([X,X|Xs],Ys) :-
list_withoutAdjacentDuplicates([X|Xs],Ys).
list_withoutAdjacentDuplicates([X1,X2|Xs],[X1|Ys]) :-
dif(X1,X2),
list_withoutAdjacentDuplicates([X2|Xs],Ys).
Sample query:
?- list_withoutAdjacentDuplicates([a,a,a,a,b,c,c,a,a],Xs).
Xs = [a,b,c,a] ; % succeeds, but leaves useless choicepoint(s) behind
false
Edit 2015-06-03
The following code is based on if_/3 and reified term equality (=)/3 by #false, which---in combination with first argument indexing---helps us avoid above creation of useless choicepoints.
list_without_adjacent_duplicates([],[]).
list_without_adjacent_duplicates([X|Xs],Ys) :-
list_prev_wo_adj_dups(Xs,X,Ys).
list_prev_wo_adj_dups([],X,[X]).
list_prev_wo_adj_dups([X1|Xs],X0,Ys1) :-
if_(X0 = X1, Ys1 = Ys0, Ys1 = [X0|Ys0]),
list_prev_wo_adj_dups(Xs,X1,Ys0).
Let's see it in action!
?- list_without_adjacent_duplicates([a,a,a,a,b,c,c,a,a],Xs).
Xs = [a,b,c,a]. % succeeds deterministically
In this answer we use meta-predicate foldl/4 and
Prolog lambdas.
:- use_module(library(apply)).
:- use_module(library(lambda)).
We define the logically pure predicatelist_adj_dif/2 based on if_/3 and (=)/3:
list_adj_dif([],[]).
list_adj_dif([X|Xs],Ys) :-
foldl(\E^(E0-Es0)^(E-Es)^if_(E=E0,Es0=Es,Es0=[E0|Es]),Xs,X-Ys,E1-[E1]).
Let's run the query given by the OP!
?- list_adj_dif([a,a,a,a,b,c,c,a,a],Xs).
Xs = [a,b,c,a]. % succeeds deterministically
How about a more general query? Do we get all solutions we expect?
?- list_adj_dif([A,B,C],Xs).
A=B , B=C , Xs = [C]
; A=B , dif(B,C), Xs = [B,C]
; dif(A,B), B=C , Xs = [A,C]
; dif(A,B), dif(B,C), Xs = [A,B,C].
Yes, we do! So... the bottom line is?
Like many times before, the monotone if-then-else construct if_/3 enables us to ...
..., preserve logical-purity, ...
..., prevent the creation of useless choicepoints (in many cases), ...
..., and remain monotone—lest we lose solutions in the name of efficiency.
More easily:
compress([X],[X]).
compress([X,Y|Zs],Ls):-
X = Y,
compress([Y|Zs],Ls).
compress([X,Y|Zs],[X|Ls]):-
X \= Y,
compress([Y|Zs],Ls).
The code works recursevely and it goes deep to the base case, where the list include only one element, and then it comes up, if the found element is equal to the one on his right , such element is not added to the 'Ls' list (list of no duplicates ), otherwise it is.
compr([X1,X1|L1],[X1|L2]) :-
compr([X1|L1],[X1|L2]),
!.
compr([X1|L1],[X1|L2]) :-
compr(L1,L2).
compr([],[]).