Declarative interpretation of list inversion in Prolog - prolog

I have some problem to join declarative reasoning...so I am here to ask you if my reasoning is correct or if there is something wrong or if I am missing something...
I have the following problem: Write a Prolog program that invert the element of a list
for example if I call something like:
myreverse([a,b,c,d,e],X). I have to obtain that X=[e,d,c,b,a]
I have the following solution:
naiverev([],[]).
naiverev([H|T],R):- naiverev(T,RevT),
append(RevT,[H],R).
This is my interpretation:
I have a fact that say that the inverse of an empty list is an empty list.
If the first list it is not empty, the fact it is not true and the fact it is not match so move on to the next rule.
The rule say that: the program that prove that the list R is the inverse of the list [H|T]
I can read the logic implication from right to left in the following way:
IF it is TRUE that naivrev(T, RevT) AND append(RevT, [H], R) ---> naivrev([H|T],R) it is TRUE
So (in the body of the rule) I am assuming that the "function" naivrev(T,RevT) respond TRUE if RevT is the inverse of T, FALSE otherwise.
At the same time I am assuming* that **append(RevT,[H],R) respond TRUE if R is [H|RevT], FALSE otherwise.
Then, if both the parts of the rule body are TRUE, the program can infer that the HEAD is TRUE (in this case that R is the inverse of [H|T])
Is this reasoning good or I am missing something?

Like the last two times, you have again intermixed Prolog's engine of computation with the purely declarative reading. Whenever you say, procedurally "the rule is not matched so move on" or anything like that, you're invoking Prolog's algorithm to explain what's going on, not logic. But you're getting better, because the rest of your stuff is much closer than before.
Rule 1: the reverse of the empty list is the empty list. (Same as you have.)
Rule 2: the reverse of [H|T] is the reverse of T, called RevT, appended to the list [H].
What makes this work, of course, is that Prolog will try rule 1, when it doesn't match, it will try rule 2, and recursively build up the result (in a tremendously inefficient way, as you probably realize). But this "making it go" of checking rules and choosing between them and how that process is performed, is what Prolog adds to a declarative or logical statement.
Your reading of the logical implication is correct: If naiverev(T, RevT) is true and append(RevT, [H], R) is true, then naiverev([H|T], R) is true. This is just as #false explained in your previous question, so I would say you're definitely starting to get it. Your remarks about the body being true leading to the head being true are spot on.
So good job, it looks like you're getting it. :)

Related

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!

SWI-Prolog: Understanding Infinite Loops

I am currently trying to understand the basics of prolog.
I have a knowledge base like this:
p(a).
p(X) :- p(X).
If I enter the query p(b), the unification with the fact fails and the rule p(X) :- p(X) is used which leads the unification with the fact to fail again. Why is the rule applied over and over again after that? Couldn't prolog return false at this point?
After a certain time I get the message "Time limit exceeded".
I'm not quite sure why prolog uses the rule over and over again, but since it is, I don't understand why I get a different error message as in the following case.
To be clear, I do understand that "p(X) if p(X)" is an unreasonable rule, but I would like to understand what exactly happens there.
If I have a knowledge base like this:
p(X) :- p(X).
p(a).
There is no chance to come to a result even with p(a) because the fact is below the rule and the rule is called over and over again. For this variant I receive a different error message almost instantly "ERROR: Out of local stack" which is comprehensible.
Now my question - what is the difference between those cases?
Why am I receiving different error messages and why is prolog not returning false after the first application of the rule in the above case? My idea would be that in the above case the procedure is kind of restarted each time the rule gets called and in the below case the same procedure calls the rule over and over again. I would be grateful if somebody could elaborate this.
Update: If I query p(a). to the 2nd KB as said I receive "Out of local stack", but if I query p(b). to the same KB I get "Time limit exceeded". This is even more confusing to me, shouldn't the constant be irrelevant for the infinite loop?
Let us first consider the following program fragment that both examples have in common:
p(X) :- p(X).
As you correctly point out, it is obvious that no particular solutions are described by this fragment in isolation. Declaratively, we can read it as: "p(X) holds if p(X) holds". OK, so we cannot deduce any concrete solution from only this clause.
This explains why p(b) cannot hold if only this fragment is considered. Additionally, p(a) does not imply p(b) either, so no matter where you put the fact p(a), you will never derive p(b) from these two clauses.
Procedurally, Prolog still attempts to find cases where p(X) holds. So, if you post ?- p(X). as a query, Prolog will try to find a resolution refutation, disregarding what it has "already tried". For this reason, it will try to prove p(X) over and over. Prolog's default resolution strategy, SLDNF resolution, keeps no memory of which branches have already been tried, and also for this reason can be implemented very efficiently, with little overhead compared to other programming languages.
The difference between an infinite deduction attempt and an out of local stack error error can only be understood procedurally, by taking into account how Prolog executes these fragments.
Prolog systems typically apply an optimization that is called tail call optimization. This is applicable if no more choice-points remain, and means that it can discard (or reuse) existing stack frames.
The key difference between your two examples is obviously where you add the fact: Either before or after the recursive clause.
In your case, if the recursive clause comes last, then no more choice-points remain at the time the goal p(X) is invoked. For this reason, an existing stack frame can be reused or discarded.
On the other hand, if you write the recursive clause first, and then query ?- q(X). (or ?- q(a).), then both clauses are applicable, and Prolog remembers this by creating a choice-point. When the recursive goal is invoked, the choice-point still exists, and therefore the stack frames pile up until they exceed the available limits.
If you query ?- p(b)., then argument indexing detects that p(a) is not applicable, and again only the recursive clause applies, independent of whether you write it before or after the fact. This explains the difference between querying p(X) (or p(a)) and p(b) (or other queries). Note that Prolog implementations differ regarding the strength of their indexing mechanisms. In any case, you should expect your Prolog system to index at least on the outermost functor and arity of the first argument. If necessary, more complex indexing schemes can be constructed manually on top of this mechanism. Modern Prolog systems provide JIT indexing, deep indexing and other mechanisms, and so they often automatically detect the exact subset of clauses that are applicable.
Note that there is a special form of resolution called SLG resolution, which you can use to improve termination properties of your programs in such cases. For example, in SWI-Prolog, you can enable SLG resolution by adding the following directives before your program:
:- use_module(library(tabling)).
:- table p/1.
With these directives, we obtain:
?- p(X).
X = a.
?- p(b).
false.
This coincides with the declarative semantics you expect from your definitions. Several other Prolog systems provide similar facilities.
It should be easy to grasp the concept of infinite loop by studying how standard repeat/0 is implemented:
repeat.
repeat :- repeat.
This creates an infinite number of choice points. First clause, repeat., simply allows for a one time execution. The second clause, repeat :- repeat. makes it infinitely deep recursion.
Adding any number of parameters:
repeat(_, _, ..., _).
repeat(Param1, Param2, ..., ParamN) :- repeat(Param1, Param2, ..., ParamN).
You may have bodies added to these clauses and have parameters of the first class having meaningful names depending on what you are trying to archive. If bodies won't contain cuts, direct or inherited from predicates used, this will be an infinite loop too just as repeat/0.

Logical OR in Prolog

I´m new in Prolog learning and tried out a logical OR-operator for that context:
%a.
b.
foo:- a ; b.
I have commented a. to try out the logical OR-Operator but it does not work. If you query with ?-foo. you get an exception. Prolog only checks the first term but not the second. Can anyone help me please?
Best regards.
You get the exception as a/0 is undefined. In order to check the or-operator you could explicitely define a as being false.
a:-false.
b.
foo:- a ; b.
Now ?- foo. gives the answer true.
Prolog makes a 'closed world' assumption. It can only evaluate the trueness or falseness of a predicate that is defined. That means that you need at least one clause that has the predicate on its left hand side (or a fact, as this will be interpreted as a clausel with no condition thus without a right hand side).
This is essentially equivalent to:
foo :- a.
foo :- b.
The first clause tries to assess whether foo is true or not, by evaluating whether a is true or not. a doesn't even exist in the database, therefore you get an error.

Declarative interpretation of list concatenation program in Prolog

I have this very simple problem: write a Prolog program that implement the append Prolog function, that concatenate two strings and that work in the following way:
append([a,b],[c,d],X). ---> X = [a,b,c,d]
append([a,b],X,[a,b,c,d]). ---> X = [c,d]
append([a,b],[X,d],[a,b,c,d]). ---> X=c
append(X,Y,[a,b,c,d]). ---> X=[] and Y=[a,b,c,d)
So I have the following two solutions and I am not so sure if my declarative interpretation is correct:
1) SOLUTION 1:
myappend1([],L,L).
myappend1([X|L1],L2,[X|L3]) :- myappend1(L1,L2,L3).
I think that I can read it in a declarative way as following:
The fact say that: if the first list (L1) is empty and the second list (L2) is not empty then it is TRUE that the concatenation of L1*L2 is L2
If the fact it is not true it means that the first list is not empty and so the concatenation of the first list and the second list it is not true that is the second list
So, let me call the first list L1, the second list L2 and the third list L3 then the rule responds TRUE if L3 is the concatenation of L1 and L2, false otherwise
I think that the declarative meaning of this rule is that: the head of the rule is true if the body of the rule is true.
In the head extract the first X element from the L1 list and from L3 list (and try to unify, if it matching go ahead, otherwise it means that the third list it is not the concatenation for the first and the second list)
In the body call the function on the first list without X element, the second list and the L3 list (that represent the concatenation)
When it reach the base case in which I have the demonstrated fact myappend1([],L,L). that is true, the program do backtracking at the previous past and because the X element of the first list unified with the X element of the third list it can do that also this computational pass it is TRUE and go back until reach the first assertion
Is this a correct declarative interpretation?
2) SECOND SOLUTION:
myappend2([],L,L).
myappend2(L1,L2,L3) :- L1=[X|T], % Dimostra questo predicato AND
L3=[X|L4], % Dimostra questo predicato AND
myappend2(T,L2,L4). % Dimostra questa funzione
As in the previous solution the fact simply say that: if the first list (L1) is empty and the second list (L2) is not empty then it is TRUE that the concatenation of L1*L2 is L2
If the fact it is not true it means that the first list is not empty and so the concatenation of the first list and the second list it is not true that is the second list
If the fact it is not true Prolog call the rule and this rule means that: the head of the rule is true if the body of the rule is true.
In this case I can read it in this way:
The concatenation of L1 and L2 is L3 is TRUE if it is true that:
The current first X element of L1 unifies with the current first element of concatenation list and myappend2 called on the first sublist, L2 and the third sublist it is true
Is it correct?
for me it is so difficult reasoning in declarative way :-(
Like last time, you're adding restrictions that aren't present in the code. Don't feel bad about it, Prolog is very different and it will take time to get used to it.
Let's start.
append([], L, L).
You said:
If the first list (L1) is empty and the second list (L2) is not empty then it is TRUE that the concatenation of L1*L2 is L2
In fact this rule says nothing about whether L2 is empty--or even a list!--or not. It simply says that the empty list appended to something else is that something else. Observe:
?- append([], foo, X).
X = foo.
The declarative reading here is "the empty list appended to L is L."
If the fact it is not true it means that the first list is not empty and so the concatenation of the first list and the second list it is not true that is the second list
Yes, this is correct, but Prolog isn't probing that deeply into the body. It just says "the first list is not empty, so this rule does not match; moving on."
The next rule:
myappend1([X|L1], L2, [X|L3]) :- myappend1(L1,L2,L3).
Your commentary seems excessively complex to me. I would say that this rule says: "myappend1 of the list [X followed by L1] to L2 is the list [X followed by L3], if myappend1 of the list L1 to L2 is L3." The consequences of this reading, however, are exactly as you describe.
Your understanding of what is happening in the first version is, therefore, correct.
The second solution is, mechanically, exactly the same as the first solution. The only difference is that we have moved the unification from the head of the clause into the body. This version is, to my eyes, clearly inferior, because all it has done is create extra work for the reader.
I think the problem you're having, so far, is that your declarative reasoning is intimately tied up with Prolog's engine of computation. A purer declarative reading like the ones I have supplied are simpler and look more like what the Prolog is saying (and have less to do with how it is evaluated).
It will take practice for you to separate these notions, but I think it will help you get better (clearly it's something you're concerned about). In the meantime there's nothing wrong with coming here and asking for help like you've been doing when you get confused. :)
Let me know if I can help more!
When you try to figure out the declarative meaning of a predicate, you are asking: For which solutions does this predicate hold?
Prolog's1 clauses contribute to the set of solutions independently. So making any connections between the clauses needs some extra scrutiny. It is very easy to make some assumptions that are not the case:
myappend1([],L,L).
If the fact it is not true it means that the first list is not empty and so ...
Consider a goal, myappend1([],[],[a]). The fact does not apply, still the first list is empty. Here, you are attempting to operationalize the meaning of the clause. It is very tempting to do this since the largest part of programming languages can only be understood by imagining how something happens step-by-step. The difficulty in Prolog lies in trying to ignore such details, without entirely ignoring procedural aspects.
myappend1([X|L1],L2,[X|L3]) :- myappend1(L1,L2,L3).
To read rules, in particular recursive rules, it is helpful to look at the :- which is a 1970s rendering of ← . So it is an arrow, but it goes from right-to-left. Therefore, you can read this rules as follows, starting with the right-hand-side:
Provided that myappend(L1,L2,L3) holds, now cross the :- over to the left side also myappend([X|L1],L2,[X|L3]) holds.
Sometimes, an even better way to read such a rule is to cover the head completely and ask
??? :- myappend1(L1,L2,L3).
Assume, I know some L1, L2, L3 that hold for myappend1(L1,L2,L3). what can I conclude out of this? Is there anything interesting? Is there anything related I can construct easily out of those 3 values?
This is something which is in the beginning a bit revolting, because you might say: But how do I know that such exists? Well, you don't. You are only assuming it exists. If it will never exist, then you will never be able to make that conclusion.
Many try to read the rules left-to-right, but while Prolog is actually executing them left-to-right, the meaning they cover is easier to understand going in the direction of the conclusion. When Prolog executes a rule left-to-right it does not know if this will work out or not. So the execution might be of entirely speculative nature. Think of append(L1,[z],[a,b,c,d,e]). Here, Prolog will apply this rule for each element of the list. But all such application is in vain. That is, ultimately it will fail.
Fine print
1 Actually, the pure, monotonic subset of Prolog.

DCG and left recursion

I am trying to implement a dcg that takes a set of strings of the form {a,b,c,d}*.The problem i have is if I have a query of the form s([a,c,b],[]),It returns true which is the right answer but when i have a query of the form s([a,c,f],[]),It does not return an answer and it runs out of local stack.
s --> [].
s --> s,num.
num --> [a].
num--> [b].
num--> [c].
num--> [d].
Use phrase/2
Let's try phrase(s,[a,b,c]) in place of s([a,b,c],[]). The reason is very simple: In this manner we are making clear that we are using a DCG (dcg) and not an ordinary predicate. phrase/2 is the "official" interface to grammars.
So your first question is why does phrase(s,[a,c,f]) not terminate while phrase(s,[a,b,c]) "gives the right answer" — as you say. Now, that is quick to answer: both do not terminate! But phrase(s,[a,b,c]) finds a solution/answer.
Universal termination
These are two things to distinguish: If you enter a query and you get an answer like true or X = a; you might be interested to get more. Usually you do this by entering SPACE or ;ENTER at the toplevel. A query thus might start looping only after the first or several answers are found. This gets pretty confusing over time: Should you always remember that this predicate might produce an answer ; another predicate produces two and only later will loop?
The easiest way out is to establish the notion of universal termination which is the most robust notion here. A Goal terminates iff Goal, false terminates. This false goal corresponds to hitting SPACE indefinitely ; up to the moment when the entire query fails.
So now try:
?- phrase(s,[a,c,f]), false.
loops.
But also:
?- phrase(s,[a,b,c]), false.
loops.
From the viewpoint of universal termination both queries do not terminate. In the most frequent usage of the words, termination is tantamount to universal termination. And finding an answer or a solution is just that, but no kind of termination. So there are queries which look harmless as long as you are happy with an answer but which essentially do not terminate. But be happy that you found out about this so quickly: It would be much worse if you found this out only in a running application.
Identify the reason
As a next step let's identify the reason for non-termination. You might try a debugger or a tracer but most probably it will not give you a good explanation at all. But there is an easier way out: use a failure-slice. Simply add non-terminals {false} into your grammar ; and goals false into predicates. We can exploit here a very beautiful property:
If the failure-slice does not terminate then the original program does not terminate.
So, if we are lucky and we find such a slice, then we know for sure that termination will only happen if the remaining visible part is changed somehow. The slice which is most helpful is:
?- phrase(s,[a,b,c]), false
s --> [], {false}.
s --> s, {false}, num.
There is not much left of your program! Gone is num//0! Nobody cares about num//0. That means: num//0 could describe anything, no matter what — the program would still loop.
To fix the problem we have to change something in the visible part. There is not much left! As you have already observed, we have here a left recursion. The classical way to fix it is:
Reformulate the grammar
You can easily reformulate your grammar into right recursion:
s --> [].
s --> num, s.
Now both queries terminate. This is the classical way also known in compiler construction.
But there are situations where a reformulation of the grammar is not appropriate. This simple example is not of this kind, but it frequently happens in grammars with some intended ambiguity. In that case you still can:
Add termination inducing arguments
?- Xs = [a,b,c], phrase(s(Xs,[]), Xs).
s(Xs,Xs) --> [].
s([_|Xs0],Xs) --> s(Xs0,Xs1), num, {Xs1=Xs}.
Inherently non-terminating queries
Whatever you do, keep in mind that not every query can terminate. If you ask: »Tell me all the natural numbers that exist – really all of them, one by one!« Then the only way to answer this is by starting with, say, 0 and count them up. So there are queries, where there is an infinity of answers/solutions and we cannot blame poor Prolog to attempt to fulfill our wish. However, what we most like in such a situation is to enumerate all solutions in a fair manner. We can do this best with a grammar with good termination properties; that is, a grammar that terminates for a list of fixed length. Like so:
?- length(Xs, M), phrase(s, Xs).
For about other examples how to apply failure-slices, see tag failure-slice.
I don't know if this is any help, because the prolog I'm using seems to have a very different syntax, but I just wrote the following program to try and match yours and it works ok.
Program
s([]).
s([X|Xs]) :- num(X), s(Xs).
num(a).
num(b).
num(c).
num(d).
Output
?- [prologdcg].
% prologdcg compiled 0.00 sec, 2,480 bytes
true.
?- s([a,c,b]).
true.
?- s([a,c,f]).
false.
Run using SWI-prolog.

Resources