Join two list in prolog? - prolog

I am learning prolog, what I am doing is writing a predicate to join two list. For example, if I query:
joinL([22,33,44],[1,2,3],L)
It will show L = [22,33,44,1,2,3].
To do it, I have tried to write predicate as followings:
joinL([],L2,L2).
joinL([H|T],L2,L):-joinL(T,L2,L),L = [H|L].
But when I query
joinL([22,33,44],[1,2,3],L)
It does not show desired result as i have just described above. Actually, it returns false.
What I want to ask is: "How did my code become wrong?", I do NOT ask "How to write predicate that join two list in prolog?" cause I can google it easily, and when compare with my code, I curiously want to know why i am wrong with my code. Can any one help me! Thank you all for reading and answering my question!

The problem is that you are using the = in the same way as one would use assignment:
L = [H|L]
In a state-changing language this means that whatever is stored in L (which is supposed to be a list) becomes a new list, made by tacking H to the front: [H|L]
In Prolog this states that what we know about L is that it is equal to [H|L]- equal to itself with H tacked to the front. This is not possible for any L though (actually, it is, if L is an infinite list containing only H but the proof engine of Prolog is not good enough to deal with that). Prolog's proof search fails at that hurdle and will return "false" - there are no solutions to the logic program you have entered.
(More after a coffee)
Here is how to think about this:
Ok, so I would like to state some logic facts about the problem of "list concatenation" so that, based on those logic facts, and given two completely-specified lists L1, L2, Prolog's proof search can determine enough about what the concatenated list LJ should look like to actually output it completely!
We decide to specify a predicate joinL(list1,list2,joinedlist) to express this.
First, we cover a special edge case:
joinL([],L2,LJ) :- LJ = L2.
So, it is stated that the 'joinL' relationship between the empty list '[]' and the joined list 'LJ' is such that 'LJ' is necessarily equal to 'L2'.
The logical reading is:
(LJ = L2) → joinL([],L2,LJ)
The operational reading is:
In order to prove joinL([],L2,LJ) you must prove LJ = L2 (which can either be verified if LJ and L2 are already known or can be added to the solution's known constraints if not.
There is also the reading of the SLD resolution, where you add the negation of joinL([],L2,LJ) to your set of logic facts, then try to prove ⊥ (the contradiction also know as the empty statement) using resolution, but I have not found that view to be particularly helpful.
Anyway, let's state more things about the edge cases:
joinL([],L2,LJ) :- LJ = L2.
joinL(L1,[],LJ) :- LJ = L1.
joinL([],[],LJ) :- LJ = [].
This will already enable the Prolog proof engine to determine LJ completely whenever any of the L1 and L2 is the empty list.
One commonly abbreviates to:
joinL([],L,L).
joinL(L,[],L).
joinL([],[],[]).
(The above abbreviation would not be possible in Picat for example)
And the third statement can be dropped because the other two "subsume it" - they cover that case more generally. Thus:
joinL([],L,L).
joinL(L,[],L).
Now for the case of non-empty lists. A fat part of logic programming is about inductive (or recursive) definitions of predicates (see this), so let's go:
joinL([H|T],L2,LJ) :- LJ = [H|LX], joinL(T,L2,LX).
Again, this is just a specification, where we say that the concatenation of a nonempty list [H|T] and any list L2 is a list LJ such that LJ is composed of H and a list LX and LX is the concatenation of T and L2.
This is useful to the Prolog proof engine because it gives more information about LJ (in fact, it specifies what the first element of LJ is) and reduces the problem to finding out more using the same predicate but a problem that is a little nearer to the base case with the empty list: joinL(T,L2,LX). If the proof goes down that route it will eventually hit joinL([],L2,LX), find out that L2 = LX and be able to successfully return from its descent.
joinL([H|T],L2,LJ) :- LJ = [H|LX], joinL(T,L2,LX).
is commonly abbreviated to
joinL([H|T],L2,[H|LX]) :- joinL(T,L2,LX).
Looks like we have covered everything with:
joinL([],L,L).
joinL(L,[],L).
joinL([H|T],L2,[H|LX]) :- joinL(T,L2,LX).
We can even drop the second statement, as it is covered by the recursive descent with L2 always equal to '[]'. It gives us a shorter program which burns cycles needlessly when L2 is '[]':
joinL([],L,L).
joinL([H|T],L2,[H|LX]) :- joinL(T,L2,LX).
Let's test this. One should use unit tests but I can't be bothered now and will just run these in SWISH. Let's see what Prolog can find out about X:
joinL([],[],X). % X = []
joinL([1,2],[],X). % X = [1,2]
joinL([],[1,2],X). % X = [1,2]
joinL([3,4],[1,2],X). % X = [3,4,1,2]
joinL([1,2],[3,4],X). % X = [1,2,3,4]
One can constrain the result completely, transforming Prolog into a checker:
joinL([3,4],[1,2],[3,4,1,2]). % true
joinL([3,4],[1,2],[1,1,1,1]). % false
Sometimes the predicate works backwards too, but often more careful design is needed. Not here:
joinL([3,4],L2,[3,4,1,2]). % L2 = [1, 2]
For this one, Prolog suggests a second solution might exist but there is none of course:
joinL(L1,[3,4],[1,2,3,4]). % L1 = [1, 2]
Find me something impossible:
joinL(L1,[3,4],[1,2,100,100]). % false

Related

What's wrong with Prolog's append?

According to my university's course in logic we could expect a different outcome than defined by Prolog for the following query:
append([], a, X)
(which unifies for X=a).
However I don't get what they're aiming at? What should be expected as a valid response, given that append should unify X for (in this example) the concatenation of [] and a?
I assume they may be expecting a return of false or [a]; however I suppose that should be the result of concatenating a and [], not [] and a (since [] is the tail of [a]).
The point here is that we expect append/3 to hold only for lists.
In the query you show, a is not a list, yet append/3 still holds.
Thus, the relation is in fact more general than we would initially expect: It holds for other cases too!
The reason why this is so can be soon from the first clause of the traditional definition of append/3:
append([], Bs, Bs).
This clause alone already makes the query succeed! No additional pure clause can prevent this. Thus, it is this clause that must be restricted if we want the relation to hold only for lists. This means, we must put a constraint on the second argument, which we do by stating it in the body of the clause:
append([], Bs, Bs) :- ... (left as an exercise)
This obviously comes at a price: Performance.
So, the trade-off here is between performance and precision. In Prolog, we often accept such a trade-off because we implicitly use such predicates only with the intended terms. On the other hand, for many predicates, we want to benefit from domain errors or type errors if they are not called with the expected types.
Your course is aiming at a very important point of Prolog programming.
Manuals are often quite sloppy on the precise definition of append/3 and similar predicates. In fact, the complete definition is so complex that it is often preferred to define only part of the actual relation. Consider the first definition in the Prolog prologue:
append(Xs, Ys, Zs) is true if Zs is the concatenation of the lists Xs and Ys.
Note the if. The definition thus gives cases, where the relation holds but does not explicitly exclude further cases. To exclude further cases, it would say iff instead. The cases mentioned (that we are talking about lists) are the intended use of the predicate. So which cases now may be additionally included? Those cases where the precondition (that the arguments are lists) does not hold.
Consider a definition of append/3 with 'iff' in place of 'if':
append([], Xs, Xs) :-
list(Xs).
append([X|Xs], Ys, [X|Zs]) :-
append(Xs, Ys, Zs).
list([]).
list([X|Xs]) :-
list(Xs).
The cost for appending two lists is now |Xs|+|Ys|. That is quite an overhead compared to |Xs| alone.
But the situation is even worse. Consider the query:
?- append([1,2], Ys, Zs).
; Ys = [], Zs = [1,2]
; Ys = [_A], Zs = [1,2,_A]
; Ys = [_A,_B], Zs = [1,2,_A,_B]
; ... .
So we get infinitely many answers to this query. Contrast this to the usual definition:
?- append([1,2], Ys, Zs).
Zs = [1,2|Ys].
There is a single answer only! It contains all the answers for all lists plus some odd cases as you have observed. So the usual definition for append has better termination properties. In fact, it terminates if either the first or the third argument is a list of known length1.
Note that the answer contains Ys. In this manner infinitely many answers can be collapsed into a single one. This in fact is the power of the logical variable! We can represent with finite means infinitely many solutions. The price to pay are some extra solutions2 that may lead to programming errors. Some precaution is thus required.
1 It also terminates in some further obscure cases like append([a|_],_,[b|_]).
2 append([a], Zs, Zs). produces (in many systems) an answer, too.
However I don't get what they're aiming at?
Knowing exactly what they are aiming at is of course impossible without asking them.
Nevertheless I think they aim to show that Prolog is (more or less) untyped. append/3 is documented as:
append(?List1, ?List2, ?List1AndList2)
List1AndList2 is the concatenation of List1 and List2.
So clearly one expects that the three arguments are lists and a is not a list. a is not the concatenation of [] and a since one would consider the two not "concatenatable".
Now this still succeeds, because append/3 is usually implemented as:
append([],T,T).
append([H|T],T2,[H|R]) :-
append(T,T2,R).
So if you give it append([],a,X)., it will simply unify with the first clause and unify X = a.
The same "weird" behavior happens with append([14],a,X). Here X = [14|a] which is not a list as well. This is because the Prolog interpreter does not "know" it is working with lists. For Prolog [A|B] is the same like any other functor.
A more "type safe" way to handle this could be:
append([],[],[]).
append([H|T],T2,[H|R]) :-
append(T,T2,R).
append([],[H|T],[H|R]) :-
append([],T,R).
Or more elegantly:
list([]).
list([_|T]) :-
list(T).
append([],T,T) :-
list(T).
append([H|T],T2,[H|R]) :-
append(T,T2,R).
since here we check whether the second argument is a list. The downside however is that now we will append/3 in O(m+n) with m the length of the first list and n the length of the second list whereas in the original code it would take only O(m) time. Furthermore note that Prolog will not raise a warning/error at parse time. It will only fail to append [] with a at the moment you query these.
Not checking types results in the fact that you have less guarantees if the program compiles/does not raises errors when you feed it to an interpreter. This can be a good thing, but a problem might be that you call some predicates in a way they don't expect which may raise errors eventually later. That is why statically typed languages are sometimes used: they "guarantee" (at least to some extent) that if you call the problem, no such errors will occur. Of course that does not mean that the program cannot error on other things (or simply make no sense). haskell for instance is statically typed and has an append like:
(++) [] t2 = t2
(++) (h:t) t2 = h:((++) t t2)
The definition is "more or less" the same, but Haskell will derive that the type of (++) is (++) :: [a] -> [a] -> [a]. Because it know the type of the input and output of every function, it can perform calculus on it, and therefore at compile time, it will raise errors if you would give (++) something different than a list.
Whether that is a good thing is of course a different question: dynamically typed programming languages are designed that way deliberately since it allows more flexibility.

Prolog and limitations of backtracking

This is probably the most trivial implementation of a function that returns the length of a list in Prolog
count([], 0).
count([_|B], T) :- count(B, U), T is U + 1.
one thing about Prolog that I still cannot wrap my head around is the flexibility of using variables as parameters.
So for example I can run count([a, b, c], 3). and get true. I can also run count([a, b], X). and get an answer X = 2.. Oddly (at least for me) is that I can also run count(X, 3). and get at least one result, which looks something like X = [_G4337877, _G4337880, _G4337883] ; before the interpreter disappears into an infinite loop. I can even run something truly "flexible" like count(X, A). and get X = [], A = 0 ; X = [_G4369400], A = 1., which is obviously incomplete but somehow really nice.
Therefore my multifaceted question. Can I somehow explain to Prolog not to look beyond first result when executing count(X, 3).? Can I somehow make Prolog generate any number of solutions for count(X, A).? Is there a limitation of what kind of solutions I can generate? What is it about this specific predicate, that prevents me from generating all solutions for all possible kinds of queries?
This is probably the most trivial implementation
Depends from viewpoint: consider
count(L,C) :- length(L,C).
Shorter and functional. And this one also works for your use case.
edit
library CLP(FD) allows for
:- use_module(library(clpfd)).
count([], 0).
count([_|B], T) :- U #>= 0, T #= U + 1, count(B, U).
?- count(X,3).
X = [_G2327, _G2498, _G2669] ;
false.
(further) answering to comments
It was clearly sarcasm
No, sorry for giving this impression. It was an attempt to give you a synthetic answer to your question. Every details of the implementation of length/2 - indeed much longer than your code - have been carefully weighted to give us a general and efficient building block.
There must be some general concept
I would call (full) Prolog such general concept. From the very start, Prolog requires us to solve computational tasks describing relations among predicate arguments. Once we have described our relations, we can query our 'knowledge database', and Prolog attempts to enumerate all answers, in a specific order.
High level concepts like unification and depth first search (backtracking) are keys in this model.
Now, I think you're looking for second order constructs like var/1, that allow us to reason about our predicates. Such constructs cannot be written in (pure) Prolog, and a growing school of thinking requires to avoid them, because are rather difficult to use. So I posted an alternative using CLP(FD), that effectively shields us in some situation. In this question specific context, it actually give us a simple and elegant solution.
I am not trying to re-implement length
Well, I'm aware of this, but since count/2 aliases length/2, why not study the reference model ? ( see source on SWI-Prolog site )
The answer you get for the query count(X,3) is actually not odd at all. You are asking which lists have a length of 3. And you get a list with 3 elements. The infinite loop appears because the variables B and U in the first goal of your recursive rule are unbound. You don't have anything before that goal that could fail. So it is always possible to follow the recursion. In the version of CapelliC you have 2 goals in the second rule before the recursion that fail if the second argument is smaller than 1. Maybe it becomes clearer if you consider this slightly altered version:
:- use_module(library(clpfd)).
count([], 0).
count([_|B], T) :-
T #> 0,
U #= T - 1,
count(B, U).
Your query
?- count(X,3).
will not match the first rule but the second one and continue recursively until the second argument is 0. At that point the first rule will match and yield the result:
X = [_A,_B,_C] ?
The head of the second rule will also match but its first goal will fail because T=0:
X = [_A,_B,_C] ? ;
no
In your above version however Prolog will try the recursive goal of the second rule because of the unbound variables B and U and hence loop infinitely.

Prolog: redundant program points in failure-slice?

We are implementing diagnostic tools for explaining unexpected universal non-termination in pure, monotonic Prolog programs—based on the concept of the failure-slice.
As introduced in
the paper "Localizing and explaining reasons for nonterminating logic programs with failure slices", goals false/0 are added at a number of program points in an effort to reduce the program fragment sizes of explanation candidates (while still preserving non-termination).
So far, so good... So here comes my question1:
Why are there N+1 program points in a clause having N goals?
Or, more precisely:
How come that N points do not suffice? Do we ever need the (N+1)-th program point?
Couldn't we move that false to each use of the predicate of concern instead?
Also, we know that the program fragment is only used for queries like ?- G, false.
Footnote 1: We assume each fact foo(bar,baz). is regarded as a rule foo(bar,baz) :- true..
Why are there N+1 program points in a clause having N goals? How come that N points do not suffice?
In many examples, not all points are actually useful. The point after the head in a predicate with a single clause is such an example. But the program points are here to be used in any program.
Let's try out some examples.
N = 0
A fact is a clause with zero goals. Now even a fact may or may not contribute to non-termination. As in:
?- p.
p :-
q(1).
p.
q(1).
q(2).
We do need a program point for each fact of q/1, even if it has no goal at all, since the minimal failure slice is:
?- p, false.
p :-
q(1),
p, false.
q(1).
q(2) :- false.
N = 1
p :-
q,
p.
p :-
p.
q :-
s.
s.
s :-
s.
So here the question is: Do we need two program points in q/0? Well yes, there are different independent failure slices. Sometimes with false in the beginning, and sometimes at the end.
What is a bit confusing is that the first program point (that is the one in the query) is always true, and the last is always false. So one could remove them, but I think it is clearer to leave them, as a false at the end is what you have to enter into Prolog anyway. See the example in the Appendix. There, P0 = 1, P8 = 0 is hard coded.

Declarative interpretation of a Prolog program that says if a list S is a sublist of another list L

I am new in Prolog and I am studying it for an universitary exam, we use SWI Prolog
I have some problem to understand how work this simple program that say TRUE if a list S is a sublist of a list L, otherwise say that the predicate is FALSE.
I have the following solution but I have some problem to understand it's declarative meaning
Reading the book I think that I had have some idea but I am not sure about it...
This is the solution that use concatenation:
sublist(S,L) :- conc(L1, L2, L),
conc(S, L3, L2).
conc([],L,L).
conc([X|L1],L2,[X|L3]) :- conc(L1,L2,L3).
This solution use an other litle program that respond TRUE if the third list is the concatenation of the first and the second list.
To say if S i sublist of L have to be TRUE the following two conditions:
L have to be a list that is the concatenation of L1 and L2
L2 have to be a list that is the concatenation of S (my sublist if exist into L list) and another list L3
This is the book explaination but it is just a litle obsucre for me...
I have try to reasoning about it and try to understand what really deeply mean...
So I think that, in some way, it is like to search if an element is member of a list using this other program:
member2(X, [X|_]).
member2(X,[_|T]):- member2(X,T).
In this program I simply say that if X is the element in the top of the list (its head) then X is in the list and the program respond true. Otherwise, if X element is not in the top of the list (or it is not my solution) I try to search it it the TAIL T of this list.
Back to the sublist program I think that the reasoning is similar
First I decompose L list in two list L1 and L2 (using conc program)**
Then I check if it is true that the concatenation of S and L3 is the L2 list.
If booth these condition it is true then S is sublist of L
I think that the L1 list have a similar role of the X element that I extract from the list in the member program.
Since the sublist S can start at the beginning of the list L, L1 can be [] and I have that I can decompose L in the concatenation of L1=[] and L2 and the I can try to decompose L2 in S and L3.
If I can do this last decomposition then the program end and I can say that it is true that S is a sublist of the original list L
If it is not true that conc(S, L3, L2) then ddo backtrack and take an other branch of computation
Is it right my declarative interpretation?
I am finding great difficulties with this example, I have also try to find a procedural explaination (using the operation trace in the Prolog shell) but I have big problem because the computation it is so big also for a short list...
The book explanation is more declarative, because it doesn't invoke Prolog's search mechanism. I would probably write this with more underscores:
sublist(S, L) :- append(_, Suffix, L), append(S, _, Suffix).
This at least makes the relationship between S and L2 (renamed Suffix) a little more clear. What we're trying to say, and this is hard to express clearly in declarative English, is that S is a sublist of L if there is a suffix of L called Suffix and S is a prefix of Suffix. Naming the other constituents only adds confusion. Prolog will internally name these variables and unify something with them as it attempts to unify everything else, but it won't share that information with the caller. Though these variables need to exist in some sense, they aren't germane to your formula or they would not be singletons. Whenever you get a singleton variable warning, replace the variable with the underscore. It will add clarity.
It happens that since the prefixes and suffixes involved can be empty lists, S can be a proper prefix of L or a proper suffix of L and everything will work out.
The declarative reading of member/2, for reference, is X is a member of a list if X is the head of the list or if X is a member of the tail of the list. Note carefully what is absent: mention of checking, success or failure, or, really, any order of operations. It is equally declarative to say X is a member of a list if it is a member of the tail or if it is the head. It is just an unavoidable fact of life that to make a computer perform a calculation it must be done in a certain order, so you have to tell Prolog things in the right order or it will enter infinite loops, but this is not an aspect of logic, just Prolog.
As we've gone over several other times, when you invoke the machinery of Prolog, you are no longer in a declarative reading. So when you say, for instance "First I decompose..." you've already left the declarative world and entered the procedural world. The declarative world doesn't have steps, even though Prolog must do things in a certain order to perform a computation on a real-life computer. Likewise, in a declarative reading you do not check things, they simply are or are not. The word backtrack also cannot appear as part of a declarative reading. The only "verb" you should be using in a declarative reading is the verb of being, "is."
That said, your Prolog/procedural readings are perfectly correct.

Prolog: temporary list storage

I'm new to Prolog and I'm stuck on a predicate that I'm trying to do. The aim of it is to recurse through a list of quads [X,Y,S,P] with a given P, when the quad has the same P it stores it in a temporary list. When it comes across a new P, it looks to see if the temporary list is greater than length 2, if it is then stores the temporary list in the output list, if less than 2 deletes the quad, and then starts the recursion again the new P.
Heres my code:
deleteUP(_,[],[],[]).
deleteUP(P,[[X,Y,S,P]|Rest],Temp,Output):-
!,
appends([X,Y,S,P],Temp,Temp),
deleteUP(P,[Rest],Temp,Output).
deleteUP(NextP,[[X,Y,S,P]|Rest],Temp,Output):-
NextP =\= P,
listlen(Temp,Z),
Z > 1, !,
appends(Temp,Output,Output),
deleteUP(NextP,[_|Rest],Temp,Output).
listlen([], 0).
listlen([_|T],N) :-
listlen(T,N1),
N is N1 + 1.
appends([],L,L).
appends([H|T],L,[H|Result]):-
appends(T,L,Result).
Thanks for any help!
Your problem description talks about storing, recursing and starting. That is a very imperative, procedural description. Try to focus first on what the relation should describe. Actually, I still have not understood what minimal length of 2 is about.
Consider to use the predefined append/3 and length/2 in place of your own definitions. But actually, both are not needed in your example.
You might want to use a dedicated structure q(X,Y,S,P) in place of the list [X,Y,S,P].
The goal appends([X,Y,S,P],Temp,Temp) shows that you assume that the logical variable Temp can be used like a variable in an imperative language. But this is not the case. By default SWI creates here a very odd structure called an "infinite tree". Forget this for the moment.
?- append([X,Y,S,P],Temp,Temp).
Temp = [X, Y, S, P|Temp].
There is a safe way in SWI to avoid such cases and to detect (some of) such errors automatically. Switch on the occurs check!
?- set_prolog_flag(occurs_check,error).
true.
?- append([X,Y,S,P],Temp,Temp).
sto. % ERROR: lists:append/3: Cannot unify _G392 with [_G395,_G398,_G401,_G404|_G392]: would create an infinite tree
The goal =\=/2 means arithmetical inequality, you might prefer dif/2 instead.
Avoid the ! - it is not needed in this case.
length(L, N), N > 1 is often better expressed as L = [_,_|_].
The major problem, however, is what the third and fourth argument should be. You really need to clarify that first.
Prolog variables can't be 'modified', as you are attempting calling appends: you need a fresh variables to place results. Note this code is untested...
deleteUP(_,[],[],[]).
deleteUP(P,[[X,Y,S,P]|Rest],Temp,Output):-
!,
appends([X,Y,S,P],Temp,Temp1),
deleteUP(P, Rest, Temp1,Output). % was deleteUP(P,[Rest],Temp,Output).
deleteUP(NextP,[[X,Y,S,P]|Rest],Temp,Output1):-
% NextP =\= P, should be useless given the test in clause above
listlen(Temp,Z),
Z > 1, !, % else ?
deleteUP(NextP,[_|Rest],Temp,Output),
appends(Temp,Output,Output1).

Resources