List exercise on prolog - list

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

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

Finding an intersection in Prolog using list predicates

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.

Prolog remove non-duplicates

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