I moved on to Prolog it after learning propositional and predicate logic.
I was wondering if someone could clarify the syntax for me as I am having trouble reading it.
I can read something like the below easily. So it is saying
X is the descend of Y if X is the child of Y.
Then it goes on to say
X is the descend of Y if X is the child of Z and Z is the descend of Y.
descend(X,Y) :-
child(X,Y).
descend(X,Y) :-
child(X,Z),
descend(Z,Y).
But once I got into looking into list programming I am struggling to read the syntax of the predicates. For example the below
remove([], _, []).
remove([X | Xs], X, Ys) :- remove(Xs, X, Ys).
remove([Y | Xs], X, [Y | Ys]) :- X \== Y, remove(Xs, X, Ys).
From testing it, it removes the second item, so for instance if I type in
remove([a,b,c], b, Ys).
I would get Ys = [a,c].
But I do not know how to read the syntax, if someone could break it down for me, that would be great.
As you have some background in logic you might find it helpful to read the rules as logic formulas. Let's replace (\==)/2 by dif/2 then remove/3 reads as follows:
remove([], _, []) ← true
remove([X | Xs], X, Ys) ← remove(Xs, X, Ys)
remove([Y | Xs], X, [Y | Ys]) ← dif(X,Y) ∧ remove(Xs, X, Ys)
Note how the implication arrow points to the head of the rule. That means the body of the rule is the antecedent and the head of the rule is the consequent. So you read a rule as: if the body of the rule is true then the head of the rule is true. The goals in the rules are connected by conjunction. Note how the fact has true as antecedent, meaning that remove([], _, []) is always true. On the other hand, if the body of a rule is false that rule fails but the predicate may still succeed if the body of another rule is true. If the bodies of all other rules are false as well then the predicate fails. So having several rules for a predicate constitutes logical or: the predicate remove/3 succeeds if rule1 OR rule2 OR rule3 succeeds.
As you asked for syntax specifically, it is also opportune to be familiar with the head and tail notation for lists. That is, you can write the first element(s) of a list explicitly then a list constructor | and the rest of the list:
[1|Xs] ... list starts with 1 and then there is a rest
[1,2|Xs] ... list starts with 1 and 2 followed by a rest
[X|Xs] ... list has at least one element followed by a rest
Note how list elements are separated by , while the rest of the list is separated by |. The term after the | is actually a list and can also be an empty list. Here are some examples for equal lists:
[1] is the same as [1|[]]
[1,2] = [1|[2]] = [1|[2|[]]] = [1,2|[]]
For the following list there are already 8 ways to write it:
[1,2,3] = [1,2|[3]] = [1|[2,3]] = [1|[2|[3|[]]]] = ...
With the above observations in mind you would then examine the rules one at a time. As #lurker already did that in his answer I will not go into detail. However, I would add that if a rule has several goals, like the 3rd rule in your example, I find it helpful to walk through the goals one at a time:
remove([Y | Xs], X, [Y | Ys]) :-
Element Y of the original list is also in the list without X IF...
remove([Y | Xs], X, [Y | Ys]) :-
dif(X,Y),
... X is different from Y AND...
remove([Y | Xs], X, [Y | Ys]) :-
dif(X,Y),
remove(Xs, X, Ys).
... the relation holds for Xs, X and Ys as well.
So why the replacement? The built-in predicate (\==)/2 just succeeds or fails without unification or side-effect. It is good for testing term inequality at a given time but does not have any effect later on. Consider the following queries:
?- X=Y, X\==Y.
no
First the variables X and Y are unified and subsequently the test for inequality fails. But:
?- X\==Y, X=Y.
X = Y
First the test for inequality succeeds, otherwise Prolog would not even consider the second goal. Then X and Y are unified successfully. This is what I mean by no effect later on. So everything I wrote above on reading predicates is not really meaningful with (\==)/2.
As a shorter version I throw the following in the hat, using if_/3 and =/3:
list_without_element([],[],_E).
list_without_element([X|Xs],L,E) :-
if_(X=E,L=Ys,L=[X|Ys]),
list_without_element(Xs,Ys,E).
The fact that you need to test it just to see what it does says that remove/3 probably isn't that well named. remove is pretty generic and leaves open the question "remove what?". Also, it is imperative, and Prolog wants to be relational. Perhaps a better name would be list_without/3 which says that the list in the 3rd argument is the list in the first argument, but without the second argument.
Be that as it may, let's read what you have.
Reading the your remove/3 predicate could be done as follows:
remove([], _, []).
The empty list is still the empty list if I remove any element.
remove([X | Xs], X, Ys) :- remove(Xs, X, Ys).
The list Ys is the list [X|Xs] with the X elements removed if Ys is the list I get after removing all X elements from Xs.
remove([Y | Xs], X, [Y | Ys]) :- X \== Y, remove(Xs, X, Ys).
[Y|Ys] is the list [Y|Xs] with the X elements removed if X is not the same as Y and Ys is the list I get after removing all X elements from Xs.
As an exercise, you should try read these again, but using a more relational name, such as the example renaming I provided.
Related
Trying to figure out how to return a list which contains ancestors of person A until person B. For example, I have the following facts:
parent(john,paul).
parent(paul,henry).
parent(henry,helen).
I can use the following code to find the ancestor of Y
ancestor(X,Y):-parent(X,Y).
ancestor(X,Y):-parent(X,Z), ancestor(Z,Y).
And I want to have a function list(X,Y,L) which will return the list of ancestors between X, Y.
Ex, List(john,helen,L) will return L = [paul, henry]
Based on the previous code, I know the Z is the value needed. But I do not know how to insert these value into a list and return.
I tried this but does not work as expected:
list([]).
ancestorList(X,Y,L):- parent(X,Y).
ancestorList(X,Y,L):- parent(P,Y), list(Old), L = [P | Old], ancestorList(X,P,L).
Any help will be appreciated.
If it must hold that
ancestorList( john, helen, L) :- L = [paul, henry], L = [paul | [henry ] ].
then it must also hold that
ancestorList( paul, helen, L) :- L = [ henry], L = [henry | [] ] . % and,
ancestorList( henry, helen, L) :- L = [] .
But we also know that
ancestorList( henry, helen, L) :- parent( henry, helen), L = [] .
Thus we know that
% Parent, Child, List
ancestorList( Henry, Helen, L) :- parent( Henry, Helen), L = [] .
% Ancestor, Descendant, List
ancestorList( Paul, Helen, L) :- parent( Paul, Henry), L = [ Paul | T ] ,
ancestorList( Henry, Helen, T ) .
This will create the list which is almost what you want. You can make it be exactly so by changing one name in the above definition.
Based on your approach, you - like many other people that start working in Prolog - aim to program in Prolog as an "imperative language".
In Prolog you can not reassign a variable. If you write L = [], then this means that, unless you backtrack, L will always be the empty list. So calling L = [P|Old] later on, will result in false, since unficiation will never yield that [] and [_|_] are equal.
You thus can not "create" a list by first initializing it to [] and then later "altering" it, since altering is (or well should) not be possible. There are some noteworthy exceptions (like adding facts with assert/1, but these are typically "bad design").
Before implementing a predicate, it is better to first design an inductive definition that specifies the logical relation you aim to implement. Then you can translate this definition into a predicate.
An inductive definition here could look like:
The ancestorList(X, Z, L) of two persons X and Z is [X] given parent(X, Z) holds; and
The ancestorList(X, Y, L) of two persons X and Y is a list that starts with X given parent(X, Y) hols, and the rest of the list is the ancestorList/3 of Y up to Z.
Once we have this inductive definition, we can translate this into code. The "skeleton" of this look like:
ancestorList(X, Z, ___):-
___.
ancestorList(X, Z, ___) :-
parent(X, Y),
___.
with the ___ that still need to be filled in.
Given there aren no infinite parent/2 chains, we know that this program will not get stuck in an infinite loop, and eventually fail if there is no chain of parents between the two given ones.
A minimal edit fix to your code, while following the ancestor predicate as you indeed wanted to, could be
% (* ancestor(X,Y) :- parent(X,Y). *)
% (* ancestor(X,Y) :- parent(X,Z), ancestor(Z,Y). *)
ancestor_list(X,Y,L) :- parent(X,Y), L = [].
ancestor_list(X,Y,L) :- parent(X,Z), L = [Z | Next], ancestor_list(Z,Y,Next).
The Prolog way of building lists is top-down, not just bottom-up like in most other functional languages (it can do that too, but top-down is neater, more efficient). So we indeed "insert" the value, Z, at the top of the list L = [Z | Next] being built, and the recursive call ancestor_list(Z,Y,Next) completes that Next list until the base case which ends it with the [] as it should, thus creating the list
[Z1 , Z2 , Z3 , ...., ZN ]
[Z1 | [Z2 | [Z3 | .... [ZN | []] .... ]]]
Next1
Next2 ....
NextN_1 % (N-1)th Next
NextN
after N recursive calls. The list itself is not "returned" from the last recursive call, but it is set up by the very first call, and the rest of the recursive calls finish "setting" (unifying, really) its elements up one by one.
See also:
Tail recursion modulo cons
tailrecursion-modulo-cons tag-info
I am trying to make a list from a tuple of variable size. However, I am having trouble figuring out how to represent an empty tuple (or a tuple with a single value in it), which I need as my end case.
This is what I have right now which, judging by the trace, does create a list (reversed however but it's not really a problem) but it fails at the very end.
tuple_to_list((), []).
tuple_to_list((X, ()), [X]).
tuple_to_list((X, XS), List) :-
tuple_to_list(XS, [X|List]).
Just :
tuple_to_list((X, XS),[X | List]) :-
tuple_to_list(XS, List).
tuple_to_list((X), [X]):-
X \= (_,_).
Last clause X \= (,). because of
?- (X) = (a,b).
X = (a, b).
I have a set of facts set/2 where the first variable is the identifier for the set and the second is the value associated with the identifier.
For example:
set(a,2).
set(a,c).
set(a,1).
set(a,a).
set(a,3).
set(a,b).
I need to construct a predicate ordering/2 (using the repeat operator) which will output the values of a specific set in their lexicographic order.
For example
?- ordering(a,Output).
Would result in
[1,2,3,a,b,c].
What I have made thus far is this code:
ordering(Input,Output):-
findall(X,set(Input,X),List),
repeat,
doSort(List)
sort(List, OrderedList),
Output = OrderedList.
The idea here is that the predicate will find all values of the set associated with the specific Input and unify the List variable with them. Now we have the unsorted List. Here comes the part I'm not completely sure on. The predicate is supposed to keep using some sort of specific doSort on the List, then check the List with sort/2 and if it's lexicographically ordered, unify it with the Output.
Can anyone clarify if I'm on the correct path and if yes then how should the doSort be implemented?
Alright, I tried a sort of answer for this using #Daniel lyon's help:
ordering(Input,Output):-
findall(X,set(Input,X),List),
repeat,
permutation(List,PermutationList),
sort(PermutationList, SortedList),
Output= SortedList , !.
The general idea is the same, for the repeat cycle, the predicate will unify the List with PermutationList, try all variants of it and check for their correctness with sort/2 until it achieves the correct permutation, unifying it with SortedList, after that it will unify the Output with SortedList. The cut is there so I will only get the Output once.
?- % init test DB
| maplist([X]>>assert(set(a,X)), [c,b,a,1,2,3]).
true.
?- % find first
| set(a,X), \+ (set(a,Y), Y #< X).
X = 1 ;
false.
?- % find next, given - hardcoded here as 1 - previous
| set(a,X), X #> 1, \+ (set(a,Y), Y #> 1, Y #< X).
X = 2 ;
false.
now we can try to make these queries reusable:
ordering(S,[H|T]) :- first(S,H), ordering(S,H,T).
first(S,X) :- set(S,X), \+ (set(S,Y), Y #< X).
next(S,P,X) :- set(S,X), X #> P, \+ (set(S,Y), Y #> P, Y #< X).
ordering(S,P,[X|T]) :- next(S,P,X), ordering(S,X,T).
ordering(_,_,[]).
To be correct, we need a cut somewhere. Can you spot the place ?
I want to write predicate which can count all encountered number:
count(1, [1,0,0,1,0], X).
X = 2.
I tried to write it like:
count(_, [], 0).
count(Num, [H|T], X) :- count(Num, T, X1), Num = H, X is X1 + 1.
Why doesn't work it?
Why doesn't work it?
Prolog is a programming language that often can answer such question directly. Look how I tried out your definition starting with your failing query:
?- count(1, [1,0,0,1,0], X).
false.
?- count(1, Xs, X).
Xs = [], X = 0
; Xs = [1], X = 1
; Xs = [1,1], X = 2
; Xs = [1,1,1], X = 3
; ... .
?- Xs = [_,_,_], count(1, Xs, X).
Xs = [1,1,1], X = 3.
So first I realized that the query does not work at all, then I generalized the query. I replaced the big list by a variable Xs and said: Prolog, fill in the blanks for me! And Prolog did this and reveals us precisely the cases when it will succeed.
In fact, it only succeeds with lists of 1s only. That is odd. Your definition is too restricted - it correctly counts the 1s in lists where there are only ones, but all other lists are rejected. #coder showed you how to extend your definition.
Here is another one using library(reif) for
SICStus|SWI. Alternatively, see tfilter/3.
count(X, Xs, N) :-
tfilter(=(X), Xs, Ys),
length(Ys, N).
A definition more in the style of the other definitions:
count(_, [], 0).
count(E, [X|Xs], N0) :-
if_(E = X, C = 1, C = 0),
count(E, Xs, N1),
N0 is N1+C.
And now for some more general uses:
How does a four element list look like that has 3 times a 1 in it?
?- length(L, 4), count(1, L, 3).
L = [1,1,1,_A], dif(1,_A)
; L = [1,1,_A,1], dif(1,_A)
; L = [1,_A,1,1], dif(1,_A)
; L = [_A,1,1,1], dif(1,_A)
; false.
So the remaining element must be something different from 1.
That's the fine generality Prolog offers us.
The problem is that as stated by #lurker if condition (or better unification) fails then the predicate will fail. You could make another clause for this purpose, using dif/2 which is pure and defined in the iso:
count(_, [], 0).
count(Num, [H|T], X) :- dif(Num,H), count(Num, T, X).
count(Num, [H|T], X) :- Num = H, count(Num, T, X1), X is X1 + 1.
The above is not the most efficient solution since it leaves many choice points but it is a quick and correct solution.
You simply let the predicate fail at the unification Num = X. Basically, it's like you don't accept terms which are different from the only one you are counting.
I propose to you this simple solution which uses tail recursion and scans the list in linear time. Despite the length, it's very efficient and elegant, it exploits declarative programming techniques and the backtracking of the Prolog engine.
count(C, L, R) :-
count(C, L, 0, R).
count(_, [], Acc, Acc).
count(C, [C|Xr], Acc, R) :-
IncAcc is Acc + 1,
count(C, Xr, IncAcc, R).
count(C, [X|Xr], Acc, R) :-
dif(X, C),
count(C, Xr, Acc, R).
count/3 is the launcher predicate. It takes the term to count, the list and gives to you the result value.
The first count/4 is the basic case of the recursion.
The second count/4 is executed when the head of the list is unified with the term you are looking for.
The third count/4 is reached upon backtracking: If the term doesn’t match, the unification fails, you won't need to increment the accumulator.
Acc allows you to scan the entire list propagating the partial result of the recursive processing. At the end you simply have to return it.
I solved it myself:
count(_, [], 0).
count(Num, [H|T], X) :- Num \= H, count(Num, T, X).
count(Num, [H|T], X) :- Num = H, count(Num, T, X1), X is X1 + 1.
I have decided to add my solution to the list here.
Other solutions here use either explicit unification/failure to unify, or libraries/other functions, but mine uses cuts and implicit unification instead. Note my solution is similar to Ilario's solution but simplifies this using cuts.
count(_, [], 0) :- !.
count(Value, [Value|Tail],Occurrences) :- !,
count(Value,Tail,TailOcc),
Occurrences is TailOcc+1.
count(Value, [_|Tail], Occurrences) :- count(Value,Tail,Occurrences).
How does this work? And how did you code it?
It is often useful to equate solving a problem like this to solving a proof by induction, with a base case, and then a inductive step which shows how to reduce the problem down.
Line 1 - base case
Line 1 (count(_, [], 0) :- !.) handles the "base case".
As we are working on a list, and have to look at each element, the simplest case is zero elements ([]). Therefore, we want a list with zero elements to have no instances of the Value we are looking for.
Note I have replaced Value in the final code with _ - this is because we do not care what value we are looking for if there are no values in the list anyway! Therefore, to avoid a singleton variable we negate it here.
I also added a ! (a cut) after this - as there is only one correct value for the number of occurrences we do not want Prolog to backtrack and fail - therefore we tell Prolog we found the correct value by adding this cut.
Lines 2/3 - inductive step
Lines 2 and 3 handle the "inductive step". This should handle if we have one or more elements in the list we are given. In Prolog we can only directly look at the head of the list, therefore let us look at one element at a time. Therefore, we have two cases - either the value at the head of the list is the Value we are looking for, or it is not.
Line 2
Line 2 (count(Value, [Value|Tail],Occurrences) :- !, count(Value,Tail,TailOcc), Occurrences is TailOcc+1.) handles if the head of our list and the value we are looking for match. Therefore, we simply use the same variable name so Prolog will unify them.
A cut is used as the first step in our solution (which makes each case mutually exclusive, and makes our solution last-call-optimised, by telling Prolog not to try any other rules).
Then, we find out how many instances of our term there are in the rest of the list (call it TailOcc). We don't know how many terms there are in the list we have at the moment, but we know it is one more than there are in the rest of the list (as we have a match).
Once we know how many instances there are in the rest of the list (call this Tail), we can take this value and add 1 to it, then return this as the last value in our count function (call this Occurences).
Line 3
Line 3 (count(Value, [_|Tail], Occurrences) :- count(Value,Tail,Occurrences).) handles if the head of our list and the value we are looking for do not match.
As we used a cut in line 2, this line will only be tried if line 2 fails (i.e. there is no match).
We simply take the number of instances in the rest of the list (the tail) and return this same value without editing it.
I am trying to create an included_list(X,Y) term that checks if X is a non-empty sublist of Y.
I already use this for checking if the elements exist on the Y list
check_x(X,[X|Tail]).
check_x(X,[Head|Tail]):- check_x(X,Tail).
And the append term
append([], L, L).
append([X | L1], L2, [X | L3]) :- append(L1, L2, L3).
to create a list, in order for the program to finish on
included_list([HeadX|TailX],[HeadX|TailX]).
but I am having problems handling the new empty list that I am trying to create through "append" (I want to create an empty list to add elements that are confirmed to exist on both lists.)
I have found this
sublist1( [], _ ).
sublist1( [X|XS], [X|XSS] ) :- sublist1( XS, XSS ).
sublist1( [X|XS], [_|XSS] ) :- sublist1( [X|XS], XSS ).
but it turns true on sublist([],[1,2,3,4)
Since you're looking for a non-contiguous sublist or ordered subset, and not wanting to include the empty list, then:
sub_list([X], [X|_]).
sub_list([X], [Y|T]) :-
X \== Y,
sub_list([X], T).
sub_list([X,Y|T1], [X|T2]) :-
sub_list([Y|T1], T2).
sub_list([X,Y|T1], [Z|T2]) :-
X \== Z,
sub_list([X,Y|T1], T2).
Some results:
| ?- sub_list([1,4], [1,2,3,4]).
true ? a
no
| ?- sub_list(X, [1,2,3]).
X = [1] ? a
X = [2]
X = [3]
X = [1,2]
X = [1,3]
X = [1,2,3]
X = [2,3]
(2 ms) no
| ?- sub_list([1,X], [1,2,3,4]).
X = 2 ? a
X = 3
X = 4
(2 ms) no
Note that it doesn't just tell you if one list is a sublist of another, but it answers more general questions of, for example, What are the sublists of L? When cuts are used in predicates, it can remove possible valid solutions in that case. So this solution avoids the use of cut for this reason.
Explanation:
The idea is to generate a set of rules which define what a sublist is and try to do so without being procedural or imperative. The above clauses can be interpreted as:
[X] is a sublist of the list [X|_]
[X] is a sublist of the list [Y|T] if X and Y are different and [X] is a sublist of the list T. The condition of X and Y different prevents this rule from overlapping with rule #1 and greatly reduces the number of inferences required to execute the query by avoiding unnecessary recursions.
[X,Y|T1] is a sublist of [X|T2] if [Y|T1] is a sublist of T2. The form [X,Y|T1] ensures that the list has at least two elements so as not to overlap with rule #1 (which can result in any single solution being repeated more than once).
[X,Y|T1] is a sublist of [Z|T2] if X and Z are different and [X,Y|T1] is a sublist of T2. The form [X,Y|T1] ensures that the list has at least two elements so as not to overlap with rule #2, and the condition of X and Z different prevents this rule from overlapping with rule #3 (which can result in any single solution being repeated more than once) and greatly reduces the number of inferences required to execute the query by avoiding unnecessary recursions.
Here is what you an do:
mysublist(L,L1):- sublist(L,L1), notnull(L).
notnull(X):-X\=[].
sublist( [], _ ).
sublist( [X|XS], [X|XSS] ) :- sublist( XS, XSS ).
sublist( [X|XS], [_|XSS] ) :- sublist( [X|XS], XSS ).
Taking a reference from this:
Prolog - first list is sublist of second list?
I just added the condition to check if it was empty beforehand.
Hope this helps.
If order matters. Example [1,2,3] is sublist of [1,2,3,4] but [1,3,2] not.
You can do something like this.
sublist([],L).
sublist([X|L1],[X|L2]):- sublist(L1,L2)
I would use append :
sublist(X, []) :-
is_list(X).
sublist(L, [X | Rest]) :-
append(_, [X|T], L),
sublist(T, Rest).
Basically we can check if M is a sublist of L if M exists in L by appending something on its back and/or its front.
append([], Y, Y).
append([X|XS],YS,[X|Res]) :- append(XS, YS, Res).
sublist(_, []).
sublist(L, M) :- append(R, _, L), append(_, M, R).