Prolog remove non-duplicates - list

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.
. %

Related

Understanding print at the end of list when removing duplicates

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/

Prolog - Filter list with only even numbers [duplicate]

This question already has an answer here:
Prolog - Multiply the even elements of a list by a number (F)
(1 answer)
Closed 1 year ago.
I have que next Prolog function that only multiply any number (F) you enter with every element in the list[], for example input: multiplyf(2, [5,4,8], N). output: [10,8,16]. This function works properly (you can try it) but now i need to filter only the even numbers to multyply F keeping the odd numbers the same, example before: input: multiplyf(2, [5,4,8], N). output: [5,8,16], Any idea how i could filter this list? I try with many ways but i didn't get the answer.
multiplyf(_,[],[]):-!.
multiplyf(F,[X|Xs], [Y|Ys]):-
Y is F*X,
multiplyf(F, Xs, Ys), !.
I try with:
even(X) :-
Y is mod(X,2),
Y =:= 0.
But when i inserted even(X) in the first function it doesn't work too.
Thank you.
While we're code golfing, how about this:
multiplyf(_F, [], []).
multiplyf(F, [X | Xs], [Y | Ys]) :-
Y is X + ((1 - (X mod 2)) * X * (F - 1)),
multiplyf(F, Xs, Ys).
Depending on whether X mod 2 is 0 or 1, Y is computed as X + X * (F - 1), i.e., X * F, or as X + 0 * Something, i.e., X.
There you go:
?- multiplyf(3, [1, 2, 3, 4, 5], Ys).
Ys = [1, 6, 3, 12, 5] ;
false.
This leaves a choice point on my old version of SWI-Prolog. Are newer versions more willing to index on a non-first argument?
The other answer is OK in spirit but in the details it is a mess. Maybe #Nicholas Carey was just trying to provoke me ;-)
You can test for odd and even using a bitmask on the least significant bit of an integer.
odd(X) :- X /\ 0x1 =:= 1.
even(X) :- X /\ 0x1 =:= 0.
There are other ways to achieve the same but this is the one with least typing. Read the docs on is/2 to understand the pitfalls of writing 0 is something something.
If you want to avoid cuts altogether you can use the following well-understood and documented approach that takes advantage of clause indexing on the first argument.
mf(Factor, Xs, Ys) :-
mf_1(Xs, Factor, Ys).
mf_1([], _, []).
mf_1([X|Xs], F, Ys) :-
LSB is X /\ 0x1,
mf_2(LSB, X, Xs, F, Ys).
mf_2(0 /* even */, X, Xs, F, [Y|Ys]) :-
Y is 2 * X,
mf_1(Xs, F, Ys).
mf_2(1 /* odd */, X, Xs, F, [X|Ys]) :-
mf_1(Xs, F, Ys).
This doesn't have cuts or ->, doesn't leave choice points, and doesn't work with floats.
To modify your multiply/3:
multiplyf( _ , [] , [] ) :- ! .
multiplyf( F ,[X|Xs] , [Y|Ys] ) :-
Y is F * X,
multiplyf(F,Xs,Ys), !.
is straightforward enough:
multiply_evens( _ , [] , [] ) .
multiply_evens( X , [Y|Ys] , [Z|Zs] ) :- even(Y), Z is X * Y , multiply(X,Ys,Zs) .
multiply_evens( X , [Y|Ys] , [Y|Zs] ) :- odd(Y), multiply_evens(X,Ys,Zs) .
even(X) :- 0 is X mod 2 .
odd(X) :- 1 is X mod 2 .
This is also arguably the most declarative solution. Very little is left to doubt about intent.
You can condense it by using a cut (!) to eliminate a choice point (after all, the number in question isn't going to cease to be positive/negative):
multiply_evens( _ , [] , [] ) .
multiply_evens( X , [Y|Ys] , [Z|Zs] ) :- even(Y), !, Z is X * Y , multiply(X,Ys,Zs) .
multiply_evens( X , [Y|Ys] , [Y|Zs] ) :- multiply_evens(X,Ys,Zs) .
even(X) :- 0 is X mod 2 .
You can condense it even further by using a soft cut:
multiply_evens( _ , [] , [] ) .
multiply_evens( X , [Y|Ys] , [Z|Zs] ) :-
( even(Y) -> Z is X * Y ; Z is X ),
multiply(X,Ys,Zs) .
even(X) :- 0 is X mod 2 .
And there's something to be said for extracting the multiplication bit from the list traversal itself:
multiply_evens( _ , [] , [] ) .
multiply_evens( X , [Y|Ys] , [Z|Zs] ) :- multiply_even(X,Y,Z), multiply_evens(X,Ys,Zs) .
multiple_even(X,Y,Z) :- even(Y), !, Z is X * Y .
multiply_even(_,Y,Y) .
even(X) :- 0 is X mod 2 .

Prolog write predicate that decrements all elements of a list of integers

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.

Prolog. I can't mix two lists

I want to mix two lists into one. For example, [1,3,5] and [2,3,9] would yield [1,2,3,5,9].
I tried this:
mezclar( L1, L2, L3 ):-
L1 = [Cab|Cola] ,
L3 = [Cab,Cola2] ,
mezclar(L2,Cola,Cola2) .
mezclar( L1, L2, L3 ):-
L1=[] ,
L3=L2 .
But I have 2 problems.
The first problem are duplicated numbers
The second one is that I'm putting lists into the list and I dont want to.
If I execute
mezclar( [1,3,5], [2,5,9], X ).
I get
X = [1, [2, [3, [5, [5|...]]]]]
To mix two lists into one, with the resulting list being ordered and without duplicates, try:
mezclar(L1,L2,L3) :- append(L1,L2,L4), sort(L4,L3).
The query:
mezclar([1,3,5], [2,5,9], X).
will produce the result:
X = [1, 2, 3, 5, 9]
This example uses sort/2. Here is a link to the SWI documentation for sort/2:
http://eu.swi-prolog.org/pldoc/man?predicate=sort/2
Are input lists sorted? If so you merge them pretty fast.
merge([], Xs, Xs).
merge([X|Xs], [], [X|Xs]).
merge([X|Xs], [Y|Ys], [X|Zs]) :- X < Y, merge(Xs, [Y|Ys], Zs).
merge([X|Xs], [Y|Ys], [Y|Zs]) :- X > Y, merge([X|Xs], Ys, Zs).
merge([X|Xs], [X|Ys], [X|Zs]) :- merge(Xs, Ys, Zs).
Assuming that your lists are ordered, then it would seem that you are looking at a straight merge. And that's simple:
merge( [] , [] , [] ) . % merging two empty lists yields the empty list itself
merge( [] , [Y|Ys] , [Y|Ys] ) . % merging the empty list with a non-empty list yields the non-empty list
merge( [X|Xs] , [] , [X|Xs] ) . % merging a non-empty list with the empty list yields the non-empty list
merge( [X|Xs] , [Y|Ys] , [X,Y|Zs] ) :- % otherwise, when both lists are non-empty...
X #= Y , % - if X and Y compare as equal (in the standard order of terms)
merge( Xs, Ys, Zs ) . % - Take both X and Y and recurse down on the tail(s).
merge( [X|Xs] , [Y|Ys] , [X|Zs] ) :- % otherwise, when both lists are non-empty...
X #< Y , % - if X compares low to Y (in the standard order of terms)
merge( Xs, [Y|Ys], Zs ) . % - Take X and recurse down on the tail(s).
merge( [X|Xs] , [Y|Ys] , [X|Zs] ) :- % otherwise, when both lists are non-empty...
X #> Y , % - if X compares hight to Y (in the standard order of terms)
merge( [X|Xs], Ys, Zs ) . % - Take Y and recurse down on the tail(s).
The above will not eliminate duplicates from the set, nor will it unify any unbound variables. Merging [1,3,5] with [1,3,5] yields [1,1,3,3,5,5] as you would expect. If you want set-like semantics, you'll need to modify the last 3 clauses to use different comparision/unification operators.

List exercise on prolog

So my professor at campus asked us to solve this exercise but is kinda tough and I am trying for 2 days now. Anyway here it is:
I'm given a list for example [a,b,c,d,w,n,i,c,k,a,b,c,d,w] and on this list I have to find out if there is "suspect" sublist. The sublist is considered "suspect" if
1) the same sublist is in the beginning and at the end,
2) it contains "w",
3) its length is 5.
The list I give has a "suspect" sublist.
If there is a suspect sublist the program must return the sublist or if there is not the program must return [o,k].
Any ideas will be welcome, thanks a lot!
(sorry if i posted something wrong)
EDIT
So after some help here is the asnwer:
checkMessage1(L,SL):-
suspiciousMessage1(L,SL).
checkMessage1(L,[o,k]):-
not(suspiciousMessage1(L,SL)).
suspiciousMessage1(L,SL):-
append(SL,Last,L),
append(_,SL,Last),
length(SL,5),
member(w,SL).
Prolog is a declarative language: describe the solution in terms of constraints and let the engine do the work.
We can determine membership in a list thus:
contains( [X|Xs] , X ) :- ! .
contains( [_|Xs] , X ) :- contains(Xs,X) .
We can determine if a lists 1st element is identical to its last element using the built-in predicate append/3:
list_starts_and_ends_with_identical( [X|Xs] ) :-
append( [X|_] , [X] , [X|Xs] )
.
Or, if you'd rather roll your own:
list_starts_and_ends_with_identical( [A|Xs] ) :-
list_ends_with( Xs , A )
.
list_ends_with( [A] , A ) .
list_ends_with( [B,C|D] , A ) :-
list_ends_with( [C|D] , A )
.
And we can enumerate sublists of the desired length like so:
sublist_of_length( Xs, L , Ys ) :- % to enumerate sublists of the desired length,
integer(L) , % - validate that the length is an integer, and
L > 0 , % - validate that the length is positive, and
length(Ys,L) , % - construct a list of unbound variables of the desired length, and
sl(Ys,L) % - invoke the helper
. %
sl( [X|Xs] , L ) :- % to enumerate sublists,
append( L , _ , [X|Xs] ) % - simply get the prefix of the desired length
. %
sl( [_|Xs] , L ) :- % on backtracking,
sl( Xs , L ) % - just recurse down on the tail of the list, discarding the first element.
.
Then, all we have to do is assemble the parts:
suspect_sublist( Xs , L ) :- % the source list Xs contains a suspect sublist L, IF...
sublist_of_length( Xs , 5 , L ) , % - L is a sublist of Xs having length 5, and
contains( L , w ) , % - L contains the atom 'w', and
list_starts_and_ends_with_identical( L ) , % - the first element of L is identical to the last.
. % Easy!
This is a good example for using DCGs:
list_suspect(Xs, Ys) :-
length(Ys, 5),
phrase(( seq(Ys), ..., seq(Ys) ), Xs),
phrase((...,"w",...), Ys).
... --> [] | [_], ... .
seq([]) --> [].
seq([E|Es]) --> [E], seq(Es).
And here is a version using append/3 instead:
list_suspect(Xs, Ys) :-
Ys = [_,_,_,_,_],
append(Ys,Is, Xs),
append(_, Ys, Is).
append("w", _, W), % or: "w" = [C], W = [C|_]
append(_, W, Ys).
Is it better readable? I think not.
The part with the [o,k] looks a bit unnatural to me, but it would be:
list_ret(Xs, Ys) :-
list_suspect(Xs, Ys).
list_ret(Xs, Ys) :-
\+ list_suspect(Xs,_),
Ys = "ok".
one liner, using append/2
suspect(L, S) :-
length(S, 5), append([S,_,S], L), memberchk(w, S) -> true ; S = [o,k].
edit as noted by false, the definition is buggy (lacks steadfastness ?): an amended rule
suspect(L, R) :-
length(S, 5), append([S,_,S], L), memberchk(w, S) -> S = R ; R = [o,k].