I am completely confused with Prolog. I have two simple predicates that I am trying to figure out, but can't seem to get them right. First I am trying to figure out a variation of a delete predicate where I need to delete all occurrences of a given element from a List1, and see if it is equal to List2.
This is what I tried.
del(S,[P],[P]).
del(S,[S],[]).
del(S,[H1,H2|T],L2) :- H1 = S, del([H2|T],L2).
del(S,[H1,H2|T],[H1,T2]) :- H1 \= S, del([H2|T],T2).
The other predicate takes two lists and takes all occurrences of the element 2 in List1 and replaces it with 4 and sees if List1 is the same as List2. To make is simplier, there's no sublists.
These are what I have tried.
change([],[]).
change([H1,H2|T],L) :- H1 = 2, append([2],L), change([H2|T],L).
change([H1,H2|T],L) :- H1 \= 2, append(H1,L), change([H2|T],L).
I would like to know what errors I have made and perhaps an explanation of how these terms are suppose to work. Thank you.
Examination of del/3
(1) del(S,[P],[P]).
(2) del(S,[S],[]).
(3) del(S,[H1,H2|T],L2) :- H1 = S, del([H2|T],L2).
(4) del(S,[H1,H2|T],[H1,T2]) :- H1 \= S, del([H2|T],T2).
(1) This rule says that the list [P] is the list [P] with the element S removed. The problem with this rule is that S and P could be the same value, and so the rule isn't always true. If you wanted this to be true all the time (be a valid rule), you'd need to stipulate that S and P can't be instantiated to the same value:
del(S, [P], [P]) :- S \= P.
That's assuming we even need this rule, but we'll leave it here for the time being as, in this new state, it's correct.
(2) This rule says that [] is the list [S] with the element S removed. That is true.
(3) This rule says that L2 is the list [H1,H2|T] with S removed if H1 and S are unified and L2 is [H2|T] with S removed. BUT, there's a missing argument. You have del([H2|T], L2) but the del/3 is defined to take 3 arguments. We'll assume you meant S is the first argument (S is still what's being removed), so del(S, [H2|T], L2). The newly repaired rule appears to be logical.
(4) This rule says that *[H1,T2] is the list [H1,H2|T] with the element S removed if H1 is not unifiable with S and T2 is the list [H2|T] with the element S removed. This has the same problem as #3 with the missing argument to del/3 which we'll, again, assume is S, making it, del(S, [H2|T], T2). Another problem is that you have [H1,T2] which is a list of only two elements: H1 and T2. Another typo. This should be [H1|T2]. So the repaired rule seems to make sense now.
Just fixing these couple of careless errors causes your predicate to almost work! It will yield the correct result when the first argument is instantiated:
| ?- del(a, [a,b,c,a,d,a], L).
L = [b,c,d] ? a
no
Also, it can be cleaned up a bit. The H2 isn't really used in the 3rd and 4th clauses. And in the 3rd clause you can instantiate S and H1 in the head of the predicate. So those two become:
(3) del(S, [S|T], L2) :- del(S, T, L2).
(4) del(S, [H|T], [H|T2]) :- H \= S, del(S, T, T2).
The predicate fails on the list being empty. I'm not sure if this is intentional, but you could argue that del(X, [], []) should be true (an empty list results when you remove an element from the empty list). If we include this rule:
(1a) del(_, [], []).
We can now get rid of rules (1) and (2) because (3) and (4) will take care of them and recurse down to rule (1a).
Additionally, the rules still fail on this case:
| ?- del(X, [a,b,c], [a,c]).
no
It would be nice if this burped out, X = b. The problem is in clause (4) where we check H \= S which means H and S are not unifiable. This expression works against us if S is a variable because then H \= S will always fail (since S could indeed be unified to H). So we replace it with dif(H,S) to check if these terms are the same.
Some Prologs don't offer dif/2, in which case, you could substitute \== for this solution (H \== S). Our resulting rule set is:
(1) del(_, [], []).
(2) del(S, [S|T], L2) :- del(S, T, L2).
(3) del(S, [H|T], [H|T2]) :- dif(H, S), del(S, T, T2).
Let's "reread" these rules:
Any element removed from the empty list is the empty list.
The list [S|T] with the element S removed is the list L2 if L2 is the list T with the element S removed.
The list [H|T2] is the list [H|T] with the element S removed if S is different than H and T2 is the list T with the element S removed.
That looks a lot simpler although just a couple of simple reworks away from the original. It will now produce this result, which is nice:
| ?- del1(X, [a,b,c,b,d], [a,c,d]).
X = b ? ;
(1 ms) no
Examination of change/2
(1) change([],[]).
(2) change([H1,H2|T],L) :- H1 = 2, append([2],L), change([H2|T],L).
(3) change([H1,H2|T],L) :- H1 \= 2, append(H1,L), change([H2|T],L).
(1) This rule says if we change all the 2's in the empty list, we get the empty list. Sounds good.
(2) This rule says if I take the list [H1,H2|T] and I change the 2's to 4's, I get L if H1 is 2 and I append L to [2] (which will always fail since [2] is not variable - see the online docs for append/2 and append/3! append/2 is for appending lists of lists, for example) and L is [H2|T] with 2's changed to 4's. Already this isn't making any logical sense at all. If I'm changing 2's to 4's why am I appending something to [2]? We need to lose the append and simply unify L with [4|T2] where T2 is [H2|T] with its 2's changed to 4's. Or in other words:
(2) change([H1,H2|T], L) :- H1 = 2, L = [4|T2], change([H2|T], T2).
This can be simplified further using the method of incorporating the unification in the head of the clause as above. Also note, again, we have no real use for making H2 visible here. So [H2|T] can just be a T:
(2) change([2|T], [4|T2]) :- change(T, T2).
So we use a 4 instead of a 2 if there's a 2, then process the tails.
(3) This rule has the same problem (2) does with the append/2 query. We can follow the same pattern of what we did with rule (2), and fix the non-equal check:
(3) change([H|T], [H|T2]) :- H \== 2, change(T, T2).
So we just carry over the element H if it's not a 2, then process the tails. The complete change predicate looks like:
(1) change([], []).
(2) change([2|T], [4|T2]) :- change(T, T2).
(3) change([H|T], [H|T2]) :- H \== 2, change(T, T2).
Rereading the rules, seeing that they make logical sense:
Changing 2's to 4's in the empty list is the empty list.
Changing 2's to 4's in the list [2|T] is the list [4|T2] if T2 is the list T with the 2's changed to 4's.
Changing 2's to 4's in the list [H|T] is the list [H|T2] if H is not 2 and T2 is the list T with the 2's changed to 4's.
Lets start with the easiest of both: change/2. Both lists are of same length, and they are essentially the same - safe for occurrences of 2 which should be replaced by 4. So lets first defined a relation for such a pair:
exch(2, 4).
exch(X, X) :-
dif(X, 2).
You might optimize this for reasons of performance to
exch(A, B) :-
( A == 2 -> B = 4
; A \= 2 -> A = B
; A = 2, B = 4
; A = B, dif(A,2)
).
Now, your actual definition is
change(Xs, Ys) :-
maplist(exch, Xs, Ys).
or more verbosely:
change([], []).
change([A|As], [B|Bs]) :-
exch(A, B),
change(As, Bs).
To compare this definition with other definitions proposed, consider the following query:
?- change([X],[Y]).
X = 2, Y = 4
; X = Y, dif(Y, 2).
del(E, Xs, Ys) :-
tfilter(dif_truth, Xs, Ys).
dif_truth(X, Y, true) :-
dif(X, Y).
dif_truth(X, X, false).
tfilter( _, [], []).
tfilter(CT, [E|Es], Fs0) :-
call(CT,E,Truth),
( Truth = false,
Fs0 = Fs
; Truth = true,
Fs0 = [E|Fs]
),
tfilter(CT, Es, Fs).
Further uses
Related
So my problem is this i have this predicate which is n_aleatorios(El,INF,SUP,L) in which El is the lenght of the list, INF is the lower limit value of the list, SUP is the upper limit value of the list, and L is a list.
The objective of this predicate is to create a list of random integers.
The problem is that this is giving true as a result instead of unifying and giving the list as the result.
auxiliar predicates:
/*Checks if a number isnt a member of a list*/
nao_membro(_, []).
nao_membro(X, [P | R]) :- X \== P,
nao_membro(X, R).
/*Joins an integer in a sorted list, in a sorted order*/
insere_ordenado(El,[],[El]).
insere_ordenado(El,[P|R],[P|L2]) :- El >= P,
insere_ordenado(El,R,L2).
insere_ordenado(El,[P|R],[El,P|R]) :- El < P.
/*Joins a random integer in a sorted list, in a sorted order*/
junta_novo_aleatorio(L1,INF,SUP,L2) :- random_between(INF, SUP, N),
nao_membro(N,L1),
insere_ordenado(N,L1,L2).
Program:
n_aleatorios(El,INF,SUP,L) :- n_aleatorios(El,INF,SUP,[],0).
n_aleatorios(El,_,_,L,El).
n_aleatorios(El,INF,SUP,L,AC) :- AC =< El,
junta_novo_aleatorio(L,INF,SUP,L2),
AC_num is AC +1,
n_aleatorios(El,INF,SUP,L2,AC_num).
My output:
?- n_aleatorios(3, 1, 5, Lst).
true ;
false.
Expected output (for example):
?- n_aleatorios(3, 1, 5, Lst).
Lst = [2,3,5]
Really any help would be appreciated.
Corrected code:
One slight improvement here:
nao_membro(X, [P | R]) :-
X =\= P,
nao_membro(X, R).
Use "arithmetic non-equivalence" =\= instead of "structural non-equivalence" \== if we can be assured that neither X nor P are fresh. Indeed, here they are always integers.
Fixed code:
junta_novo_aleatorio(L1,INF,SUP,L2) :-
random_between(INF, SUP, N),
nao_membro(N,L1),
insere_ordenado(N,L1,L2).
n_aleatorios(Count,INF,SUP,L) :-
n_aleatorios(Count,INF,SUP,[],L).
n_aleatorios(Count,_,_,L,L) :-
length(L,Count),!.
n_aleatorios(Count,INF,SUP,Lin,Lout) :-
length(Lin,Len), Len<Count,!,
junta_novo_aleatorio(Lin,INF,SUP,Lmid),
n_aleatorios(Count,INF,SUP,Lmid,Lout).
Problems are due to the fact that the list to be constructed is not "threaded" properly between the predicates calls. Here, Lin is the "input list". It is where information "flows into" the predicate. Lin is is used to construct a new list Lout, which appears again the in the predicate argument, but now information "flows out" of the predicate back to the caller.
I have also eliminated the counter, which is already implicit in the length of Lin.
Note that selection between the two clauses of n_aleatorios/5 occurs via a guard followed by a "cut" which commits to the execution path. The second "cut" is actually not needed, because there is no clause below, but makes things clear:
n_aleatorios(Count,_,_,L,L) :-
length(L,Count),!,...(we will never try any clause below)..
n_aleatorios(Count,INF,SUP,Lin,Lout) :-
length(Lin,Len), Len<Count,!,....(we will never try any clause below)..
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.
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 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([],[]).
First of all, I am completely a beginner at Prolog.
I am trying to compare each element of a list with each element of another list. By compare, I mean sending these 2 elements to a predicate(conflicts) I wrote. So far, I got this:
%iterate over the first list
cmp_list([],_,_).
cmp_list([X|Y],[A|B],Result):-
cmp_list_inner(X,[A|B],Result),
cmp_list(Y,[A|B],Result).
%iterate over the second list
cmp_list_inner(_,[],_).
cmp_list_inner(X,[A|B],S):-
not(conflicts(X,A)), %compare
cmp_list_inner(X,B,[X-A|S]).%if okay, add their combination, i.e X-A to the main list which will be returned
The predicate cmp_list stands for the recursion of outer list, whereas the one with inner stands for inner list.
cmp_list(firstlist, secondlist, new list after the combination which will be returned.)
This doesn't work! Even though it adds the values for a single element in the first value to the main list, it doesn't append the second comparison(for the second element in the first list) to the main list which will be returned. Result should be in the form of:
[X1-Y1], [X1-Y2], [X2-Y1], [X2-Y2].... where Xs are from the first list and Ys are from the second list.
Any help would be appreciated. Thanks!
You only need one simple predicate:
cmp_list([], [], []). % cmp_list succeeds for two empty lists
cmp_list([H1|T1], [H2|T2], [H1-H2|R]) :-
% cmp_list of [H1|T1] and [H2|T2] succeeds if...
\+ conflicts(H1, H2), % conflicts fails for H1 and H2
cmp_list(T1, T2, R). % cmp_list succeeds for T1 and T2, result R
% Otherwise, cmp_list fails (e.g., lists are diff length)
A little more compact would be to use the built-in predicate, maplist:
cmp_list(L1, L2, R) :- % cmp_list succeeds on L1, L2 if...
maplist(no_conflicts, L1, L2, R). % no_conflicts succeeds for every
% corresponding pair of L1, L2 elements
no_conflicts(X, Y, X-Y) :- \+ conflicts(X-Y).
If you just want to capture all the corresponding pairs that don't conflict and ignore the rest, then:
cmp_list([], _, []).
cmp_list([_|_], [], []).
cmp_list([H1|T1], [H2|T2], R) :-
( conflicts(H1, H2)
-> cmp_list(T1, T2, R)
; R = [H1-H2|R1],
cmp_list(T1, T2, R1)
).
This uses the "if-then-else" pattern in Prolog formed by grouping -> with ;. This will create a result that looks like, [x1-y1, x3-y3, ...]. You can choose however you want to form your result elements by changing this line:
R = [H1-H2|R1]
For example, R = [[H1,H2]|R1] would yield a result that looks like, [[x1,y1], [x3,y3], ...].
For the more general problem (i.e., the one you were really looking for :)), I'll start with the original code but modify it where it's needed:
%iterate over the first list
cmp_list([], _, []).
cmp_list([X|T], L2, Result):-
cmp_list_inner(X, L2, R1),
cmp_list(T, L2, R2),
append(R1, R2, Result).
%iterate over the second list
cmp_list_inner(_, [], []).
cmp_list_inner(X, [A|B], R) :-
( conflicts(X, A)
-> cmp_list_inner(X, B, R)
; R = [X-A|T],
cmp_list_inner(X, B, T)
).