Creating list of all elements fullfilling relation in prolog - prolog

I have a relation grandpa(X,Y) and I'm trying to get list of all grandpa's in the known world without using forall, findall and similar methods
My attempt so far is:
find_grandpas(Tmp,List):-
grandpa(New,_),
not(member(New,Tmp)),
find_grandpas([New|Tmp],List).
list_grandpas(List) :- find_grandpas([], List), print_list(List).
while checking stepswith trace I can see, that correct list is evaluated, but later it is lost. How can I construct some "cut" condition?

The problem here is that you build the right list but you need to tell where to stop. For example if we have as facts:
grandpa(a,_).
grandpa(b,_).
grandpa(c,_).
Eventually you will build list [a, b, c] and recursively try to find another solution there calling in the next iteration:
grandpa(New,_) will produce a but due to not(member(New,Tmp))this will fail, via backtracking it will try again with b... and c wher also fails. Then no choice points left and it completely fails. Soyou need to change to:
find_grandpas(Tmp,List):-
(
grandpa(New,_),
not(member(New,Tmp)) ->
find_grandpas([New|Tmp],List); List = Tmp
).
This simply says try grandpa(New,_),not(member(New,Tmp)) if succeed I have something to add, add it and go on in recursion. If not then no more solutions so set List = Tmp and this succeeds returning the right list.
Example:
?- find_grandpas([],L).
L = [c, b, a].

Related

Edit Prolog program to output the result of a computation

I've found something about this in other questions, but mine is a bit different.
Given a string, I have to output another string with no adjacent duplicates.
E.g., given [a,a,b,b,c,d,a], my output will be [a,b,c,d,a].
Now, I've wrote the following recursive program to check if a certain given string has adjacent duplicates:
notequal(A,[]).
notequal(A,[X|S]) :- not(A=X).
noadj([]):-!.
noadj([A|S]) :- notequal(A,S), noadj(S).
How would I modify it in order to output what I described? I've tried multiple times but I'm new to prolog and I can't seem to get into its logic.
Of course, I'll need another variable, which would contain an element if notequal is true for that element.
So my idea is to iterate through the list and only add a certain term to the result if it passes the "notequal" test.
I'll edit this: I finally managed to do something like that by adding
noadjlist([X|S],[X|LS]) :- notequal(X,S), noadjlist(S,LS).
noadjlist([X|S],LS) :- noadjlist(S,LS).
noadjlist([],LS):-!.
However, my results are like:
?- noadjlist([1,2,2,3],LS).
LS = [1, 2, 3|_19316] .
why do I get that uninstantiated variable at the end?
noadjlist([],LS):-!.
should be
noadjlist([],[]):-!.

Graph implementation (does graph include all vertices?)

Trying to implement following predicate, which takes on input
list of lists - one list = one tested graph T (as edges)
graph G itself (as edges)
and tests whether T includes all of the vertices of G. If it does(true) should return it. Basically it's list filtering.
What I have so far is:
covb([],G).
covb([H|R],G) :-
isItCov(G,H), covb(R,G).
isItCov([],H).
isItCov([V-W|R],H) :-
vertex(V,H),
vertex(W,H),
isItCov(R,H).
vertex(V,[V-_|G]).
vertex(V,[_-V|G]).
vertex(V,[_|G]):- vertex(V,G).
For covb([[a-c,c-b,c-d]],[a-b,a-c,a-d,c-d,b-c]) works fine(true). For covb([[a-c]],[a-b,a-c,a-d,c-d,b-c]) works fine too (false). I got an issue while I call it with multiple lists like covb([[a-c,c-b,c-d],[a-c]],[a-b,a-c,a-d,c-d,b-c]). which should work only for the first one.
I actually got two questions -
Why does it work for one list only?
I want to return items of lists of lists which passed the condition and returned true (that's the filtering part). How should I do that?
First of all, your program has a number of singleton variable warnings. Do not ignore singleton variable warnings. They can hide real bugs. Also, since more experienced Prolog users here know that programs with singleton variable warnings are not even worth running, they will (a) just see the warnings and decide that they are no longer interested in trying to help you, or (b) fix the warnings on their side, but then by definition they will be working on a program that is no longer the program you posted!
Now for your questions.
Why does it work for one list only?
It's really not clear what you are asking here, or just above with "covb([[a-c,c-b,c-d],[a-c]],[a-b,a-c,a-d,c-d,b-c]). which should work only for the first one."
This query does fail:
?- covb([[a-c,c-b,c-d],[a-c]],[a-b,a-c,a-d,c-d,b-c]).
false.
This comes down to testing each of the two lists:
?- isItCov([a-b,a-c,a-d,c-d,b-c], [a-c,c-b,c-d]).
true .
?- isItCov([a-b,a-c,a-d,c-d,b-c], [a-c]).
false.
The first list does cover the graph, while the second doesn't. Overall, your definition of covb/2 is written to succeed if all lists cover the graph. This is not the case, so your covb/2 query fails.
Was this what you wanted to know?
I want to return items of lists of lists which passed the condition and returned true (that's the filtering part). How should I do that?
You could see if your Prolog's documentation has something for the word "filter". On SWI-Prolog you can do this:
?- apropos(filter).
true.
This will point you to the include/3 predicate, which seems to do what you want:
?- include(isItCov([a-b,a-c,a-d,c-d,b-c]), [[a-c,c-b,c-d],[a-c]], Covers).
Covers = [[a-c, c-b, c-d]].
If you want to write a filter predicate for your concrete application, it might look something like this:
graph_covers(_Graph, [], []).
graph_covers(Graph, [Nodes|NodesRest], Covers) :-
( isItCov(Graph, Nodes)
-> Covers = [Nodes|CoversRest]
; Covers = CoversRest ),
graph_covers(Graph, NodesRest, CoversRest).
This is similar to your predicate, it just adds an extra argument to collect those node lists for which isItCov/2 succeeded. If it did not succeed, it continues with a list not containing that current node list.
?- graph_covers([a-b,a-c,a-d,c-d,b-c], [[a-c,c-b,c-d],[a-c]], Covers).
Covers = [[a-c, c-b, c-d]] ;
false.

Prolog program with lists and sublists

Hi I have to solve a problem in Prolog, that sounds like this: deletes all the sublists of a list that are increasing. For example the list [1,[2],[3,4],6] becomes [1,6].
So far I have tried this but it's not working. Any help please ?
domains
el=integer
list=el*
element=integer;list
lista=element*
goal
elim([1,[2],[3],4)],L),
write(L).
predicates
elim(lista,lista)
is_increasing(lista)
is_list(lista)
clauses
is_increasing([A,B|T]) :-
B>A,
is_increasing([B|T]).
is_list([_|_]).
is_list([]).
elim([],[]).
elim([E|Es],[E|Ts]) :-
is_list(E),
is_increasing(E),
elim(Es, Ts).
attempt to modularize your code: first write an is_increasing/1. Since it appears that a list of 1 element is increasing, you can do as simply as
is_increasing([A,B|T]) :- B > A, is_increasing([B|T]).
is_increasing([_]).
then you can use it to discard elements while copying. Beware to check that an element is a list before calling. Here is a possible definition
is_list([_|_]).
is_list([]).
edit
there is a bad declaration, as advised by mbratch
element=i(integer);l(list)
should be
element=integer;list
Also, you forgot is_increasing([_])., and anyway you're not using at all is_list or is_increasing.
The rule eliminating sublists of course should read
elim([E|Es], Ts) :- is_list(E), is_increasing(E), elim(Es, Ts).
just add the base case and a copy. i.e. elim is a 3 clauses predicate...
edit apart the rule above, you need only a base case
elim([],[]).
and a copy
elim([E|Es],[E|Ts]) :- elim(Es, Ts).
just try to understand why the order of rules is also important in Prolog...

Prolog Relational Tracking without Lists

I am trying to get a predicate to relate from 1 fact to another fact and to keep going until a specified stopping point.
For example,
let's say I am doing a logistics record where I want to know who got a package from who, and where did they get it from until the end.
Prolog Code
mailRoom(m).
gotFrom(annie,brock).
gotFrom(brock,cara).
gotFrom(cara,daniel).
gotFrom(daniel,m).
gotFrom(X,Y) :- gotFrom(Y,_).
So what I am trying to do with the predicate gotFrom is for it to recursively go down the list from what ever point you start (ex: gotFrom(brock,Who)) and get to the end which is specified by m, which is the mail room.
Unfortunately when I run this predicate, it reads out,
Who = annie.
Who = brock.
Who = cara.
etc.etc....
I tried stepping through the whole thing but Im not sure where it goes from brock to annie, to cara and all the way down till it cycles through trues for infinity. I have a feeling that it has something to do with the wildcard in the function (_), but Im not sure how else I could express that part of the function in order for the predicate to search for the next fact in the program instead of skipping to the end.
I tried using a backcut (!) in my program but it gives me the same error.
Any help is greatly appreciated. I don't want code I just want to know what I am doing wrong so I can learn how to do it right.
Thanks.
I'm afraid this rule is meaningless:
gotFrom(X,Y) :- gotFrom(Y,_).
There is nothing here to constrain X or Y to any particular values. Also, the presence of singleton variable X and the anonymous variable _ means that basically anything will work. Try it:
?- gotFrom([1,2,3], dogbert).
true ;
true ;
What I think you're trying to establish here is some kind of transitive property. In that case, what you want is probably more like this:
gotFrom(X,Z) :- gotFrom(X, Y), gotFrom(Y, Z).
This produces an interesting result:
?- gotFrom(brock, Who).
Who = cara ;
Who = daniel ;
Who = m ;
ERROR: Out of local stack
The reason for the problem may not be immediately obvious. It's that there is unchecked recursion happening twice in that rule. We recursively unify gotFrom/2 and then we recursively unify it again. It would be better to break this into two predicates so that one of them can be used non-recursively.
got_directly_from(annie,brock).
got_directly_from(brock,cara).
got_directly_from(cara,daniel).
got_directly_from(daniel,m).
gotFrom(X,Y) :- got_directly_from(X, Y).
gotFrom(X,Z) :- got_directly_from(X, Y), gotFrom(Y, Z).
This gives us the desired behavior:
?- gotFrom(brock, Who).
Who = cara ;
Who = daniel ;
Who = m ;
false.
Notice this one is resilient to my attack of meaningless data:
?- gotFrom([1,2,3], dogbert).
false.
Some general advice:
Never ignore singleton variable warnings. They are almost always a bug.
Never introduce a cut when you don't understand what's going on. The cut should be used only where you understand the behavior first and you understand how the cut will affect it. Ideally, you should try to restrict yourself to green cuts—cuts that only affect performance and have no observable effects. If you don't understand what Prolog is up to, adding a red cut is just going to make your problems more complex.

prolog, copying lists

I am attempting to get my arms around some basic prolog but struggling a bit in the process. In specific - I am trying to get through a list of items and copy it, item by item into a new list. I can get it to reverse, but I am finding it trickier doing it without reversing.
Ive been trying the following -
copy(L,R) :- accCp(L,R).
accCp([],R).
accCp([H|T],R) :- accCp(T,H).
When i run a trace on this - i can see the individual items being copied across, but they get 'lost', and dont form a growing list (at R, as i was hoping). How could i achivie this?
Many thanks
Your base case needs to set the copy list to empty when the original list is empty. Then, the recursive case needs to take H from list L and add it to the head of list R:
copy(L,R) :- accCp(L,R).
accCp([],[]).
accCp([H|T1],[H|T2]) :- accCp(T1,T2).
When you call copy, it works its way down to the base case, where it sets R to an empty list. Then, as it works back up, it keeps appending the head H of known list [H|T1] to the beginning of variable list [H|T2]. It does that until the original case is reached, at which point R contains a full copy of L.
Very simple approach would be:
clone1(X,X).
?-clone1([1,2,3],Z).
Z=[1,2,3]
Here's an expressive approach for the list handling. You want to copy or clone a list. My approach is add every element from the list to want to copy to another list and return another list.
clone([],[]).
clone([H|T],[H|Z]):- clone(T,Z).
OUTPUT
?- clone([1,2,3,4,5],Z).
Z=[1,2,3,4,5]
?- clone([a,b,c,d],Z).
Z=[a,b,c,d]
?- clone([ [a,1,2] , [b,2,3] , [c,3,4] ],Z).
Z = [[a, 1, 2], [b, 2, 3], [c, 3, 4]]
This works for every kind of list.

Resources