Related
I tried to answer another question (wrongly though) and this led to a question on "difference lists" (or "list differences", which seems a more appropriate name, unless "Escherian Construction" isn't preferred)
We have a fully ground list of elements obj(X,Y) (both X and Y ground). We want to retain only the first obj(X,_) where X hasn't been encountered yet when going through the list front to back. Those "first elements" must appear in order of appearance in the result.
Let's specify the problem through test cases:
% Testing
:- begin_tests(collapse_dl).
test(one) :- collapse_dl([],[]).
test(two) :- collapse_dl([obj(a,b)],
[obj(a,b)]).
test(three) :- collapse_dl([obj(a,b),obj(a,c)],
[obj(a,b)]).
test(four) :- collapse_dl([obj(a,b),obj(a,c),obj(b,j)],
[obj(a,b),obj(b,j)]).
test(five) :- collapse_dl([obj(a,b),obj(a,c),obj(b,j),obj(a,x),obj(b,y)],
[obj(a,b),obj(b,j)]).
:- end_tests(collapse_dl).
rt :- run_tests(collapse_dl).
Now, this is easy to implement using filtering, list prepend and reverse/2, but what about using difference lists and list append?
however, I'm not able to get the seen/2 predicate to work. It checks whether obj(A,_) is already in the difference list. But what's a proper termination for this predicate?
% This is called
collapse_dl([],[]) :- !.
collapse_dl([X|Xs],Out) :-
Dlist = [X|Back]-Back, % create a difflist for the result; X is surely in there (as not yet seen)
collapse_dl(Xs,Dlist,Out). % call helper predicate
% Helper predicate
collapse_dl([],Ldown,Lup):- % end of recursion; bounce proper list back up
Ldown = Lup-[]. % the "back" of the difflist is unified with [], so "front" becomes a real list, and is also Lup
collapse_dl([obj(A,_)|Objs],Ldown,Out) :-
seen(obj(A,_),Ldown), % guard: already seen in Ldown?
!, % then commit
collapse_dl(Objs,Ldown,Out). % move down chain of induction
collapse_dl([obj(A,B)|Objs],Ldown,Out) :-
\+seen(obj(A,_),Ldown), % guard: not yet seen in Ldown?
!, % then commit
Ldown = Front-Back, % decompose difference list
Back = [obj(A,B)|NewTail], % NewTail is fresh! Append via difflist unification magic
collapse_dl(Objs,Front-NewTail,Out). % move down chain of induction; Front has been refined to a longer list
% Membership check in a difference list
seen(obj(A,_),[obj(A,_)|_Objs]-[]) :- !. % Yup, it's in there. Cut retry.
seen(Obj,[_|Objs]-[]) :- ... % But now???
Update
With Paulo's code snippet:
% Membership check in a difference list
seen(Element, List-Back) :-
List \== Back,
List = [Element|_].
seen(Element, List-Back) :-
List \== Back,
List = [_| Tail],
seen(Element, Tail-Back).
So, term equivalence, or dis-equivalence in this case, is the solution!
We now pass all the test.
Try (taken from Logtalk difflist library object):
member(Element, List-Back) :-
List \== Back,
List = [Element|_].
member(Element, List-Back) :-
List \== Back,
List = [_| Tail],
member(Element, Tail-Back).
memberchk/2 should do it. Using the approach from here,
%% collapse_dl( ++Full, -Short )
collapse_dl( [obj(K,V) | A], B ) :-
memberchk( obj(K,X), B ),
( X = V -> true ; true ),
collapse_dl( A, B ).
collapse_dl( [], B ) :-
length( B, _), !.
Doing what (functional) Prolog does best, instantiating an open-ended list in a top-down manner.
Passes the tests included in the question.
Addendum: With printouts
%% collapse_dl( ++Full, -Short )
collapse_dl( [obj(K,V) | A], B ) :-
format("Enter : ~w relatedto ~w\n", [[obj(K,V) | A], B]),
% Necessarily find (find or insert) obj(K, X) (thanks to the
% uninstantiated X) in list B which has an "unobserved" tail:
memberchk( obj(K,X), B ),
% Unify X with V if you can; ignore failure if you can't!
( X = V -> true ; true ),
format("Mid : ~w relatedto ~w\n", [[obj(K,V) | A], B]),
collapse_dl( A, B ),
format("Return: ~w relatedto ~w\n", [[obj(K,V) | A], B]).
collapse_dl( [], B ) :-
format("Termination: From unobserved-tail-list ~w ",[B]),
length(B, _),
format("to ~w (and don't come back!)\n",[B]),
!.
Because of the added printouts this code is no longer tail-recursive. The original is, and so has no "return" in its trace: it just goes forward and stops working right away when the input list is traversed to its end.
See more about the distinction e.g. here.
This "open-ended list" technique is not difference list, but the two are very closely related. And we don't actually need the explicit tail anywhere here, except for the final freezing. So we just do the O(n) length call instead of the explicit O(1) Tail = [] we'd do with difference lists, no biggie.
Of bigger impact is the choice of list instead of e.g. tree data structure. Trees can be open-ended too, just need to use var/1 here and there. Next step is the tree's structure. Top-down open-leaved tree can't be rotated (as all the calls reference the same top node) so its depth will depend on the input's orderedness. To maintain good balance the trees need to be rotated on occasion, hence closed; and we're back to the traditional state-passing code, were each call gets two tree arguments -- the one before update, and the other after it: the
upd(T1, T2), next(T2, T3), more(T3, T4), ...
kind of thing. It ought to be used in real code. There are some libraries that do that.
This answer's code is simplistic, in order to be simple and illustrative.
Since I currently need it, I got a simpler solution. Assuming the
difference list is open, means for the pair List-Back, we have var(Back).
Then we can short cut, only passing List:
member_open(_, List) :- var(List), !, fail.
member_open(Element, [Element|_]).
member_open(Element, [_|List]) :- member_open(Element, List).
If we want to append an element to the List, since for example we didn't find it via member_open/2, we simply make Back = [NewElement|Back2] and continue with Back2.
Here is variables/2 (ISO term_variables/2) written this way, so that it doesn't need reverse/1:
variables(T, L) :-
variables(T, B, B, B2),
B2 = [],
L = B.
variables(V, L, B, B) :- var(V), member_open(W, L), V == W, !.
variables(V, L, [V|B], B) :- var(V), !.
variables(T, L, B, B2) :-
T =.. [_|A],
variables_list(A, L, B, B2).
variables_list([T|A], L, B, B2) :-
variables(T, L, B, H),
variables_list(A, L, H, B2).
variables_list([], _, B, B).
Seems to work:
?- variables(f(X,g(X,Y),Y), L).
L = [X, Y].
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.
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).
As a Prolog newbie, I try to define a predicate filter_min/2 which takes two lists to determine if the second list is the same as the first, but with all occurrences of the minimum number removed.
Sample queries with expected results:
?- filter_min([3,2,7,8], N).
N = [3,7,8].
?- filter_min([3,2,7,8], [3,7,8]).
true.
I tried but I always get the same result: false. I don't know what the problem is. I need help!
Here is my code:
filter_min(X,Y) :-
X == [],
write("ERROR: List parameter is empty!"),
!;
min_list(X,Z),
filter(X,Y,Z).
filter([],[],0).
filter([H1|T1],[H2|T2],Z) :-
\+ number(H1),
write("ERROR: List parameter contains a non-number element"),
!;
H1 \= Z -> H2 is H1, filter(T1,T2,Z);
filter(T1,T2,Z).
There are a couple of problems with your code:
filter([],[],0). will not unify when working with any list that does not have 0 as its minimum value, which is not what you want. You want it to unify regardless of the minimum value to end your recursion.
The way you wrote filter([H1|T1],[H2|T2],Z) and its body will make it so that the two lists always have the same number of elements, when in fact the second one should have at least one less.
A correct implementation of filter/3 would be the following:
filter([],[],_).
filter([H1|T1],L2,Z):-
\+ number(H1),
write("ERROR: List parameter contains a non-number element"),
!;
H1 \= Z -> filter(T1,T2,Z), L2 = [H1|T2];
filter(T1,L2,Z).
A bounty was offered...
... for a pure solution that terminates for (certain) cases where neither the length of the first nor of the second argument is known.
Here's a candidate implementation handling integer values, built on clpfd:
:- use_module(library(clpfd)).
filter_min(Xs,Ys) :-
filter_min_picked_gt(Xs,_,false,Ys).
filter_min_picked_gt([] ,_,true ,[]).
filter_min_picked_gt([Z|Xs],M,Picked,[Z|Zs]) :-
Z #> M,
filter_min_picked_gt(Xs,M,Picked,Zs).
filter_min_picked_gt([M|Xs],M,_,Zs) :-
filter_min_picked_gt(Xs,M,true,Zs).
Some sample queries:
?- filter_min([3,2,7,8],[3,7,8]).
true ; false. % correct, but leaves choicepoint
?- filter_min([3,2,7,8],Zs).
Zs = [3,7,8] ; false. % correct, but leaves choicepoint
Now, some queries terminate even though both list lengths are unknown:
?- filter_min([2,1|_],[1|_]).
false. % terminates
?- filter_min([1,2|_],[3,2|_]).
false. % terminates
Note that the implementation doesn't always finitely fail (terminate) in cases that are logically false:
?- filter_min([1,2|_],[2,1|_]). % does _not_ terminate
For a Prolog newbie, better start with the basics. The following works when first argument is fully instantiated, and the second is an uninstantiated variable, computing the result in one pass over the input list.
% remmin( +From, -Result).
% remmin([],[]). % no min elem to remove from empty list
remmin([A|B], R):-
remmin(B, A, [A], [], R). % remove A from B to get R, keeping [A]
% in case a smaller elem will be found
remmin([C|B], A, Rev, Rem, R):-
C > A -> remmin(B, A, [C|Rev], [C|Rem], R) ;
C==A -> remmin(B, A, [C|Rev], Rem, R) ;
C < A -> remmin(B, C, [C|Rev], Rev, R).
remmin([], _, _, Rem, R) :- reverse(Rem, R).
First, we can get the minimum number using the predicate list_minnum/2:
?- list_minnum([3,2,7,8],M).
M = 2.
We can define list_minnum/2 like this:
list_minnum([E|Es],M) :-
V is E,
list_minnum0_minnum(Es,V,M).
list_minnum0_minnum([],M,M).
list_minnum0_minnum([E|Es],M0,M) :-
M1 is min(E,M0),
list_minnum0_minnum(Es,M1,M).
For the sake of completeness, here's the super-similar list_maxnum/2:
list_maxnum([E|Es],M) :-
V is E,
list_maxnum0_maxnum(Es,V,M).
list_maxnum0_maxnum([],M,M).
list_maxnum0_maxnum([E|Es],M0,M) :-
M1 is max(E,M0),
list_maxnum0_maxnum(Es,M1,M).
Next, we use meta-predicate tfilter/3 in tandem with dif/3 to exclude all occurrences of M:
?- M=2, tfilter(dif(M),[2,3,2,7,2,8,2],Xs).
Xs = [3,7,8].
Put the two steps together and define min_excluded/2:
min_excluded(Xs,Ys) :-
list_minnum(Xs,M),
tfilter(dif(M),Xs,Ys).
Let's run some queries!
?- min_excluded([3,2,7,8],Xs).
Xs = [3,7,8].
?- min_excluded([3,2,7,8,2],Xs).
Xs = [3,7,8].
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).