Member in combination with recursion - prolog

I learn Prolog at university and keep stumbling on someting rather odd during the home excercises. I wrote following Prolog clauses, which are part of a much bigger program:
edges(X,Edges):-
findall(Edge,(highway(X,Y,Edge);highway(Y,X,Edge)),Edges).
edgesList([],_).
edgesList([node(X)|InL],OutL):-
member((node(X),Edges),OutL),
edges(X,Edges),
edgesList(InL,OutL).
Which use following facts:
highway(1,2,yellow).
highway(2,3,blue).
highway(1,3,yellow).
You can see highway as a fact that describes two nodes in the first two arguments and an edge in the third. All facts together form a connected graph.
With the clause edgesList, I want to list the edges per node e.g.
Result = [(node(1),[yellow,yellow]),(node(2),[blue,yellow]),(node(3),[blue,yellow])]
But when I write my query:
edgesList([node(1),node(2),node(3)],List).
I get following result:
List = [(node(1),[yellow, yellow]), (node(2),[blue, yellow]), (node(3),[blue, yellow])|_G610]
For some reason, Prolog won't unify the tail of the result-list with the empty list, despite the fact that the member-predicate is used correct, I assume. It's something that happend a few times now in different excercises and it would be good to know what I did wrong...

The problem is in the clause:
edgesList([],_).
because in the end it will fill the list with an uninstantiated tail (|_G610).
One solution is :
edges(X,Edges):-
findall(Edge,(highway(X,Y,Edge);highway(Y,X,Edge)),Edges).
edgesList([],[]).
edgesList([node(X)|InL],[(node(X),Edges)|T]):-
edges(X,Edges),
edgesList(InL,T).

Related

Returning List of possible answers

I have an assignment to make a "simple" chatbot and my main problem now is in a predicate that receives a list of atoms (Ex: ["good","morning"]) and should return a list of possible answers that are already defined in structures. Also the predicate needs to filter for keywords and only return answer that contain keywords from the list given.
I have tried checking if an element from the list is already defined in any of the defined answers. If yes it would concat to the returning list.
(This concat predicate was defined by the teacher and its basically the append/3 already defined).
ans("Hello there", 0).
ans("Hello",0).
ans("Hi",0).
ans("Good Morning",0).
ans("Good Afternoon",0).
ans("Good Night",0).
answer([], List).
answer([H|T], List):- ans(H, _), concat(List, H, List), answer([T], List).
When I run this with answer(["good"],List) and it runs to a infinite (I assume its a infinite loop because it takes a long time to run and give me an error saying that there no space left in stack)
In this case the output should be ["Good Morning", "Good Afternoon", "Good Night"].
I appreciate all the help I can get.
We've got a lot of issues here, starting with your question.
Without concat/3 and how you are calling your code, it really isn't possible for anyone to solve your problems. None of your predicates take a single argument, but you are saying the code goes into an infinite loop if you call it with one. So the best we can do is speculate until you improve your question with the missing detail.
Here's my speculated assumptions: concat/3 is probably append/3. You are calling answer(["good"], Ans).
Looking at your code, concat(List, H, List) looks very wrong to me, for two reasons:
You are trying to mutate List, which is not possible in Prolog (variables cannot change once bound).
You are supplying a non-list in H to be concatenated to List.
Another problem is that List never receives an initial binding, so most likely you have to call this predicate with an argument of [], which means you are treating it as both an input and an output value.
I think your intuition is on the right track here: it should be possible for you to find things by prefix using append/3. Unfortunately, SWI-Prolog does not store strings as lists any more, so there is another predicate you have to use: string_concat/3.
I don't think you can get the return value you want here easily. I think you need to use findall/3 or one of its friends to get multiple solutions, and if you are supplying a list of possible prefixes, you are going to get a list of lists of possible solutions. Perhaps I'm missing something obvious here. Anyway, this is the solution I found:
answer([], []).
answer([H|T], [Answers|R]) :-
findall(Answer,
(ans(Answer, _), string_concat(H, _, Answer)),
Answers),
answer(T, R).
This gives me the following output:
?- answer(["Good"], L).
L = [["Good Morning", "Good Afternoon", "Good Night"]].
Please correct your question so we can be more helpful to you!

Prolog: Looping through elements of list A and comparing to members of list B

I'm trying to write Prolog logic for the first time, but I'm having trouble. I am to write logic that takes two lists and checks for like elements between the two. For example, consider the predicate similarity/2 :
?- similarity([2,4,5,6,8], [1,3,5,6,9]).
true.
?- similarity([1,2,3], [5,6,8]).
false.
The first query will return true as those two lists have 5 and 6 in common. The second returns false as there are no common elements between the two lists in that query.
I CANNOT use built in logic, such as member, disjoint, intersection, etc. I am thinking of iterating through the first list provided, and checking to see if it matches each element in the second list. Is this an efficient approach to this problem? I will appreciate any advice and help. Thank you so much.
Writing Prolog for the first time can be really daunting, since it is unlike many traditional programming languages that you will most likely encounter; however it is a very rewarding experience once you've got a grasp on this new style of programming! Since you mention that you are writing Prolog for the first time I'll give some general tips and tricks about writing Prolog, and then move onto some hints to your problem, and then provide what I believe to be a solution.
Think Recursively
You can think of every Prolog program that you write to be intrinsically recursive in nature. i.e. you can provide it with a series of "base-cases" which take the following form:
human(John). or wildling(Ygritte) In my opinion, these rules should always be the first ones that you write. Try to break down the problem into its simplest case and then work from there.
On the other hand, you can also provide it with more complex rules which will look something like this: contains(X, [H|T]):- contains(X, T) The key bit is that writing a rule like this is very much equivalent to writing a recursive function in say, Python. This rule does a lot of the heavy lifting in looking to see whether a value is contained in a list, but it isn't complete without a "base-case". A complete contains rule would actually be two rules put together: contains(X, [X|_]).
contains(X, [H|T]):-contains(X, T).
The big takeaway from this is to try and identify the simple cases of your problem, which can act like base cases in a recursive function, and then try to identify how you want to "recurse" and actually do work on the problem at hand.
Pattern Matching
Part of the great thing about Prolog is the pattern matching system that it has in place. You should 100% use this to your advantage whenever you can -- it is especially helpful when trying to do anything with lists. For example:
head(X, [X|T]).
Will evaluate to true when called thusly: head(1, [1, 2, 3]) because intrinsic in the rule is the matching of X. This sort of pattern matching on the first element of a list is incredibly important and really the key way that you will do any work on lists in Prolog. In my experience, pattern matching on the head of a list will often be one of the "base-cases" that I mentioned beforehand.
Understand The Flow of the Program
Another key component of how Prolog works is that it takes a "top-down" approach to reading code. What I mean by that is that every time a rule is called (except for definitions of the form king(James).), Prolog starts at line 1 and continues until it reaches a rule that is true or the end of the file. Therefore, the ordering of your rules is incredibly important. I'm assuming that you know that you can combine rules together via a comma to indicate logical AND, but what is maybe more subtle is that if you order one rule above another, it can act as a logical OR, simply because it will be evaluated before another rule, and can potentially cause the program to recurse.
Specific Example
Now that I've gotten all of my general advice out of the way, I'll actually reference the given problem. First, I'd write my "base-case". What would happen if you are given two lists whose first elements are the same? If the first element in each list is not the same, then they have to be different. So, you have to look through the second list to see if this element is contained anywhere in the rest of the list. What kind of rule would this produce? OR it could be the case that the first element of the first list is not contained within the second at all, in which case you have to advance once in the first list, and start again with the second list. What kind of rule would this produce?
In the end, I would say that your approach is the correct one to take, and I have provided my own solution below:
similarity([H|_], [H|_]).
similarity(H1|T1], [_|T2]):- similarity([H1|T1], T2).
similarity([_|T1], [H2|T2]):- similarity(T1, [H2|T2]).
Hope all of this helps in some way!

Prolog permutation-code slightly unclear

I just have a little question about the following code I've just written.
permu([],[]).
permu([H|T],R) :- same_length([H|T],R), select(H,R,Rz), permu(T,Rz).
Result: gives out all permutation (doesn't delete redundancies, but that's not the matter; by using findall and defining another predicate I can fix that easily)
My initial code:
permu([],[]).
permu([H|T],R) :- select(H,R,Rz), permu(T,Rz).
Result: gives out the first result and ends up in endless loop, when clicking "Next". But when I decided to precisely define their length (with same_length as shown above) it suddenly worked.
I have already traced both codes, but I still don't understand why the length of both lists HAS to be defined under any circumstances.
Looking at my initial code, both lists lose one element in each step of the recursion. The list on the left gets its head cut off each run and my R variable is being shortened because of select. The recursion process ends, when both lists run out of elements in the same recursion run => due to select, both lists need to share the same number and type of elements.
What does prevent Prolog from realizing it? What makes him fall into the endless loop?
Taking this code as an example I don't have to define the length but it still works:
permu([], []).
permu([H|T], R) :- permu(T, Rz), select(H, R, Rz).
In contrast to my initial code here the select is just put after the recursion call (permu).

Prolog Smart Graph Algorithm

I am a university student who is being taught Prolog. I am only 3 weeks into the course and this is my first assignment.
The goal of the assignment is to create a graph searching algorithm that returns a path from goal to end and remembers that certain nodes are not reachable from specific nodes. The base code we must have is:
street(genoa,turin).
street(genoa,busalla).
street(milan,turin).
street(milan,rome).
street(milan,genoa).
street(genoa,rome).
street(rome,napoli).
actionStreet(A,B):- street(A,B); street(B,A).
giveSolution(actionStreet,genoa,X,Steps).
The first part of the assignment is to create a graph solving algorithm that does not have the destination in the solution more than once and I believe this code does this:
street(genoa,turin).
street(genoa,busalla).
street(milan,turin).
street(milan,rome).
street(milan,genoa).
street(genoa,rome).
street(rome,napoli).
actionStreet(A,B):- street(A,B);street(B,A).
isConnected(P,A,B):-call(P,A,B).
giveSolution(P,Start,End,Steps):-trip(P,Start,End,[Start],Steps).
trip(P,Node,Node,_,[Node]).
trip(P,Start,Finish,Visited,[Start|Path]):-
isConnected(P,Start,X),
not(member(X,Visited)),
trip(P,X,Finish,[X|Visited]).
So the bit I am stuck on is that to solve the overall problem you need to keep track of certain nodes that are unreachable from specific nodes. I think this can be done using a dynamic database, that stores the nodes and the other node that is not reachable from the first node.
Therefore when the trip(P,Start,Finish,Visited,[Start|Path]):- predicate fails, the node X and the node End being looked at should be put in the database. I thought this could be done by using this change to the trip(P,Start,Finish,Visited,[Start|Path]):- predicate code:
trip(P,Start,Finish,Visited,[Start|Path]):-
(isConnected(P,Start,X),
not(member(X,Visited)),
trip(P,X,Finish,[X|Visited])-> true;assert(cannot(X,Finish))).
However, the if then predicate does not work in the way it would in Java. Can someone please help and explain why this code fragment does not do exactly what the first version does, except add to the database if those conditions are fail?
You are inverting the 'logical truth' of your predicate. that is, when the original trip/5 succeeded, now it's failing, and when it was failing, it now succeeds and remember cannot(X,Finish). And X should be uninstantiated at that point...
Controlling execution flow of Prolog can be tricky, since it may depend on the presence of other clauses, where they are located, what these clauses do... I don't claim much merit on this clue

insert element in a list and return the same list updated

Hi i'm trying to insert an element in a list but it is very important from my program that the result is stored in the original list and not in a new one.
Any code that i have written or found on the internet only succeeds if you create a new list in which the end result is kept.
So my question is can anyone tell me how to define a function: insert(X,L) where X is an element and L is a list?
No, Prolog just doesn't work that way. There is no such thing as "modifying" a value. A variable can be unified with a specific value, but if it was already [1,3], it won't ever be [1,2,3] later.
As aschepler says, you cannot add or make any change to a proper list, i.e. a list in which every element is already bound. The only "modifying" we can do is unifying one expression with another.
However there is a concept of a partial list to which additional elements can be "added" at the end. This is typically known as a difference list, although that nomenclature may not be immediately understandable.
Suppose we start, not with an empty list, but with a free variable X. One might however think of subtracting X from X and getting "nothing". That is, an empty difference list is represented by X - X. The minus "-" here is a purely formal operator; no evaluation of the difference is intended. It's just a convenient syntax as you see from how difference lists can be used to accomplish what you (probably) want to do.
We can add an element to a difference list as follows:
insertDL(M,X-Y,X-Z) :- Y = [M|Z].
Here M is the new element we want to add, X-Y is the "old" difference list, and X-Z is the "new" difference (to which M has been added, by unifying the previously free variable Y with the partial list [M|Z], so that Z becomes the "open" tail of partial list X).
When we are finally done inserting things into our difference list, we can turn X into a proper list by setting the "free tail" at that point to the empty list [ ]. In this sense X is the "same" variable as when we first began, just unified by incremental steps from free variable to proper list.
This is a very powerful technique in Prolog programming, and it takes some practice to feel comfortable using it. Some links to further discussion on the Web:
[From Prolog lists to difference lists]
http://www.irisa.fr/prive/ridoux/ICLP91/node8.html
[Implementing difference lists in Prolog]
http://www.cl.cam.ac.uk/~jpw48/difflists.pdf
[Lecture Notes: Difference Lists]
http://www.cs.cmu.edu/~fp/courses/lp/lectures/11-diff.pdf
Some prologs provide the setarg/3 predicate in order to modify terms in place.
In order to use it over lists, you only need to consider that they are just a nice representation of chains of compound terms with functor '.'/2
In any case, when you need to use setarg/3 in Prolog, it probably means you are doing something wrong.

Resources