Related
I've been trying to learn Prolog by writing a simple program that given a list of items, returns/stores a copy of the list without duplicates in the same order. Ex: [1,1,2,3,3,3,5] returns [1,2,3,5]. I'm using the append to add the numbers to an empty list and the member to check if the integer already has been added.
remove_duplicates([], R).
remove_duplicates([H|T], R) :-
member(H, R)
-> remove_duplicates(T, R)
; append(H, R),
remove_duplicates(T, R).
I've gotten the code to almost work, however when running the code it returns R = [1, 2, 3, 6|_].
I've tried tracing and debugging however I'm unable to understand why the |_ is added at the end.
My thought process for the code is as following, please point out if I'm misunderstanding something.
remove_duplicates([], R). % If first list is empty, return R. (Used to stop the recursion).
remove_duplicates([H|T], R) :-
member(H, R)
-> remove_duplicates(T, R) % If head is member of R (=true), call remove:_duplicates again without the head.
; append(H, R),
remove_duplicates(T, R). % else (if member(H, R) = false), add the head to list R and call remove_duplicates again with tail and R.
My answer to the question implement a prolog predicate that removes all duplicate elements as noted by #brebs is close, but it will not give you what you want. This
list_set( [] , [] ) .
list_set( [X|Xs] , Ys ) :- memberchk(X,Xs), !, list_set(Xs,Ys) .
list_set( [X|Xs] , [X|Ys] ) :- list_set(Xs,Ys) .
prefers the last of duplicated items, so
[1,2,3,3,2,1]
reduces to
[3,2,1]
Which violates the constraint in your problem statement, that you get
a copy of the list without duplicates in the same order.
We can switch that up to prefer the first of any duplicate elements by using a helper predicate with an accumulator and introducing the use of append/3:
list_set( Xs , Ys ) :- list_set( Xs , [] , Ys ) .
list_set( [] , Ys , Ys ) .
list_set( [X|Xs] , Ts , Ys ) :- memberchk(X,Ts) , ! , list_set(Xs,Ts,Ys) .
list_set( [X|Xs] , Ts , Ys ) :- append(Ts,[X],T1) , list_set(Xs,T1,Ys) .
This is fastest method I've found so far (doesn't use append or reverse):
https://stackoverflow.com/a/74024975/
as the title suggests, I have to write a predicate that decreases all the values of a list. I did this but I am not convinced can you help me?
decremento([ ],[ ]).
decremento([H | T], L):- decremento(T,N), L is N-1.
So the problem statement is to traverse a list of [presumably] numbers, decrementing each one?
So... something like this —
decremento( [] , [] ) . % the list is empty, nothing to decrement
decremento( [X|Xs] , [Y|Ys] ) :- % the list is non-empty, so...
Y is X-1, % - decrement the head of the list,
decremento(Xs,Ys) % - recurse down
. % Simple!
Running the above:
decremento( [1,2,3] , X ).
yields the desired
X = [0, 1, 2]
But... what about the other way?
decremento(X,[0,1,2]).
Sadly, that give us
Arguments are not sufficiently instantiated
In:
[2] 1 is _1632-1
[1] decremento([_1694|_1696],[1,2|...]) at line 3
We need to do a little type checking to be it work both ways. So, something like this will work properly.
decremento([1,2,3],X) yields X = [0,1,2]
decremento(X,[0,1,2]) yields X = [1,2,3]
decremento([1,2,3],[0,1,2]) yields true.
decremento( Xs, Ys ) :- nonvar(Xs), !, add_list(-1,Xs,Ys) .
decremento( Xs, Ys ) :- nonvar(Ys), !, add_list(+1,Ys,Xs).
add_list( _, [] , [] ) .
add_list( N, [X|Xs] , [Y|Ys] ) :- Y is X+N, add_list(N,Xs,Ys).
From the comment to another answer:
maplist(plus(-1), Xs, Ys)
?- maplist(plus(-1), [1,2,3], Ys).
Ys = [0, 1, 2].
?- maplist(plus(-1), Xs, [1,2,3]).
Xs = [2, 3, 4].
?- maplist(plus(-1), [1,X], [Y,2]).
X = 3,
Y = 0.
I'm really new to Prolog and I am trying to make an isIntersection that gives me the intersection of two lists and puts the answer in the third list. I cannot use any Prolog list predicates because it's for a class and that's just the rules. This is what I have and I'm having trouble debugging and seeing why this implementation is wrong. Anyone have any ideas?
/* Checks if the item is in the list */
in(Item, [Item|Rest]).
in(Item, [Not|Rest]) :- in(Item, Rest).
/* Makes the intersection list */
isIntersection([], [], []).
isIntersection([H|R], List, [H|Final]) :-
in(H, List),
isIntersection(R, List, Final),
write(H).
isIntersection([Discard|Rest], List, Final) :-
isIntersection(Rest, List, Final),
write(Discard).
Prolog is a very versatile query language, so let's use Prolog to find the problem!
?- isIntersection([a,b],[c,b],Zs).
false.
This failure is not what we expect. To better localize the problem we might a) generalize the query or b) reduce input size. I will try generalizing it first:
?- isIntersection([a,b],Ys,Zs).
loops. % ERROR: Out of global stack
Seems we have no luck, but then this query would have to produce infinitely many lists for Ys so it might be OK to loop.
I could continue that way, but why not let Prolog do the thinking for me? I will try all possible lists:
?- length(L,_),append(Xs,Ys,L), isIntersection(Xs,Ys,Zs).
L = Xs, Xs = Ys, Ys = Zs, Zs = []
; L = Xs, Xs = [_A], Ys = Zs, Zs = []
; L = Xs, Xs = [_A, _B], Ys = Zs, Zs = []
; L = Xs, Xs = [_A, _B, _C], Ys = Zs, Zs = []
; L = Xs, Xs = [_A, _B, _C, _D], Ys = Zs, Zs = []
; ... .
So for each list length (so far), there is only one solution with Ys and Zs being an empty list... Is there any solution for Ys being larger?
?- length(L,_),Ys = [_|_], append(Xs,Ys,L), isIntersection(Xs,Ys,Zs).
loops.
So lets take the minimal missing example from above with Ys having one element:
?- isIntersection([],[a],[]).
false.
With this goal, now look at your code!
But there is another problem (after fixing above):
?- isIntersection([a],[a],Xs).
Xs = [a]
; Xs = [].
The rule discards any element! But it should only discard those that are not in List. So:
isIntersection([Discard|Rest], List, Final) :-
list_without(List,Discard), % maplist(dif(Discard),List)
isIntersection(Rest, List, Final).
list_without([], _).
list_without([E|Es], F) :-
dif(E, F),
list_without(Es, F).
Finally, always keep an eye on negative examples. Many attempts here (incorrectly) succeeds for queries like isIntersection([a],[a],[]).
(Your relation in/2 might better be called element_in/2)
I'd go at it something like this, sorting and merging so as to avoid the O(n2) performance:
intersection_of( Xs , Ys , Zs ) :- % to find the intersection of two sets, we
sort(Xs,X1) , % - sort the left source list, removing duplicates to ensure that it's a set
sort(Ys,Y1) , % - sort the right source list, removing duplicates to ensure that it's a set
merge(Xs,Ys,Z1) , % - merge them to find the common members (an ordered set)
( var(Zs) -> % - if the result is unbound,
Zs = Z1 ; % - simply unify the merge result with the result set
sort(Zs,Z1) % - otherwise, sort the result and match against the merge result
) . %
The merge is simple
merge( [] , [] , [] ) .
merge( [_|_] , [] , [] ) .
merge( [] , [_|_] , [] ) .
merge( [X|Xs] , [Y|Ys] , [X|Zs] ) :- X = Y , merge( Xs , Ys , Zs ) .
merge( [X|Xs] , [Y|Ys] , Zs ) :- X #< Y , merge( Xs , [Y|Ys] , Zs ) .
merge( [X|Xs] , [Y|Ys] , Zs ) :- X #> Y , merge([X|Xs] , Ys , Zs ) .
there is only a List that can match your base case, and this simple fact inhibits the whole computation.
I've been trying to write some code that takes a list of values, and removes all values which are only in the list once, the non-duplicates:
dbltaker([], []).
dbltaker([H | X], Y):-
\+mem(H, X),
dbltaker(X, Y).
dbltaker([H | X], [H | Y]):-
mem(H, X), !,
dbltaker(X, Y).
dbltaker([H | X], [H | Y]):-
mem(H, Y),
dbltaker(X, Y).
mem(H, [H | _]).
mem(H, [_ | T]):-
mem(H, T).
The trouble I've been having is that after I move a non-duplicate to the other list, it's duplicate is no longer a duplicate so isn't moved into the list. For example, the list [1, 1, 1, 2, 2, 3] gives [1, 1, 2] as the output, as the last one and two aren't considered duplicates as they're no longer members of their tails, and I can't check to see if they're members of the new list, as it's yet to be instantiated.
Is there a way around this?
Thanks.
I think the simpler way should be to should pass around to original list, to be able to check when an element is duplicate or not.
dbltaker(L, R) :- dbltaker(L, L, R).
dbltaker([], _L, []).
dbltaker([H|T], L, [H|R]) :- at_least_2(H, L), !, dbltaker(T, L, R).
dbltaker([_|T], L, R) :- dbltaker(T, L, R).
the service predicate at_least_2(H, L) can easily be implemented...
This is how I'd do it:
First, a check for list membership:
exists_in( A , [A|_] ) :- ! .
exists_in( A , [_|B] ) :- exists_in(A,B) .
Then a conditional add. If X is not contained in Y, add X to Y giving Z:
add_if_not_exists( X , Z , Z ) :- exists(X,T) , ! .
add_if_not_exists( X , Y , [X|Y] ) .
A worker predicate that does the hard work, using an accumulator (seeded to the empty list []) to build the set of distinct elements:
dedupe( [] , Z , Z ) . % if the source list is exhausted, we're done: the accumulator is the set of distinct list members.
dedupe( [X|Xs] , Y , Z ) :- % otherwise...
add_if_not_exists(X,Y,T) , % - add X to the accumulator if it's not already there.
dedupe(Xs,T,Z) % - and recurse down.
. % Easy!
And finally, the public interface predicate that simply invokes the worker predicate:
dedupe( Xs, Ys ) :- % dedupe a list
dedupe( Xs, [] , Ys ) % by invoking the helper predicate with the accumulator seeded with the empty set.
. %
Note: the worker predicate builds the deduped list in reverse order. If order is important, reversing a list is trivial:
rev( [] , [] ) .
rev( [X|Xs] , Rs ) :- rev( Xs , [X|Rs] ) .
Just modify the public interface to do the reversal:
dedupe1( Xs, Ys ) :- % dedupe a list
dedupe( Xs, [] , T ) , % by invoking the helper predicate with the accumulator seeded to the empty set.
rev(T,Ys) % and reversing the result.
. %
How I can add an element E right after the first occurrence of X in list Xs?
Example:
?- insert_right_behind(5,10,[2,4,10,12],Xs).
Xs = [2,4,10,5,12]. % expected answer
At this moment, I have problems understanding
the recursion that needs to be made since I am new to the language.
Thanks in advance!
In the previous answer most successful queries left behind useless choicepoints.
We can avoid these choicepoints by using if_/3 and (=)/3 like so:
item_following_in_inserted(I,J,[X|Xs],Ys0) :-
if_(J = X,
Ys0 = [J,I|Xs],
(Ys0 = [X|Ys], item_following_in_inserted(I,J,Xs,Ys))).
Let's run some queries!
?- item_following_in_inserted(5,10,[2,4,12],Xs).
false. % OK, unchanged
?- item_following_in_inserted(5,10,[2,4,10,12],Xs).
Xs = [2,4,10,5,12]. % succeeds deterministically
?- item_following_in_inserted(5,10,[2,4,10,12,10],Xs).
Xs = [2,4,10,5,12,10]. % succeeds deterministically
?- item_following_in_inserted(I,E,Xs,Ys).
Xs = [ E|_Z], Ys = [ E,I|_Z] % OK, unchanged
; Xs = [ _A,E|_Z], Ys = [ _A,E,I|_Z], dif(E,_A)
; Xs = [_A,_B,E|_Z], Ys = [_A,_B,E,I|_Z], dif(E,_A), dif(E,_B)
...
Use three predicate clauses:
% Inserting after in an empty list is an empty list:
insert_after( _X, _Y, [], [] ).
% If the "after" item is at the head of the list, then the "insert" item can go after it:
insert_after( X, Y, [Y|T], [Y,X|T] ).
% If the head of the list isn't the "after" item, then the result will be
% this with a new tail list that has the "insert" item inserted:
insert_after( X, Y, [H|T], [H|L] ) :-
Y \= H,
insert_after( X, Y, T, L ).
If the "after" item doesn't exist in the given list, then insert_after/4 will yield the original list. By removing the first insert_after clause above, it will just fail for that case.
Let's keep it simple and use append/3, meta-predicate maplist/2 and prolog-dif like so:
item_following_in_inserted(I,J,Xs,Ys) :-
append(Prefix,[J |Suffix],Xs),
maplist(dif(J),Prefix),
append(Prefix,[J,I|Suffix],Ys).
Done! It's query time... First, let's run the query the OP gave:
?- item_following_in_inserted(5,10,[2,4,10,12],Xs).
Xs = [2,4,10,5,12] % succeeds, but leaves behind choicepoint
; false.
What if the item is not a member of the given list?
?- item_following_in_inserted(5,10,[2,4, 12],Xs).
false. % fails, as expected: 10 is absent
Let's check that we only insert after the first occurrence—and nowhere else!
?- item_following_in_inserted(5,10,[2,4,10,12,10],Xs).
Xs = [2,4,10,5,12,10] % single solution
; false. % terminates universally
What about the most general query of item_following_in_inserted/4?
?- item_following_in_inserted(I,E,Xs,Ys).
Xs = [ E|_Z], Ys = [ E,I|_Z]
; Xs = [ _A,E|_Z], Ys = [ _A,E,I|_Z], dif(E,_A)
; Xs = [ _A,_B,E|_Z], Ys = [ _A,_B,E,I|_Z], dif(E,_A), dif(E,_B)
; Xs = [_A,_B,_C,E|_Z], Ys = [_A,_B,_C,E,I|_Z], dif(E,_A), dif(E,_B), dif(E,_C)
...