I began to study Prolog recently and faced one strange problem.
Here you can see a code example (I use SWI-Prolog 7.2.3) which gives a tree of relationships and my solution of 2 tasks.
/* File: ancestors.pl
Author: Dave Robertson
Purpose: Relationships in a family tree
Suppose we have a family tree like this :
alan andrea bruce betty eddie elsie fred freda
| | | | | | | |
|_____| |_____| |_____| |_____|
| | | |
clive clarissa greg greta
| |__________|___| | |
|__________|__| |_____________|
| | |
dave doris henry
which is defined in Prolog by the following 3 sets of predicates:
*/
% parent(Parent, Child).
% Parent is the parent of Child.
parent(alan, clive).
parent(andrea, clive).
parent(bruce, clarissa).
parent(betty, clarissa).
parent(clive, dave).
parent(clarissa, dave).
parent(clive, doris).
parent(clarissa, doris).
parent(eddie, greg).
parent(elsie, greg).
parent(fred, greta).
parent(freda, greta).
parent(greg, henry).
parent(greta, henry).
%% PROBLEM 1
%% How do you find out if someone is the ancestor of someone else ?
ancestor(X,Y) :- parent(X,Y).
ancestor(X,Y) :- parent(X,Z), ancestor(Z,Y).
%% PROBLEM 3
%% How do you know if someone is related to someone else ?
relative(X,Y) :- ancestor(X,Y).
relative(X,Y) :- ancestor(Y,X).
relative(X,Y) :- ancestor(Z,X), ancestor(Z,Y), X\==Y.
When I want to get the relatives of dave I do:
relative(dave,X).
X = clive ;
X = clarissa ;
X = alan ;
X = andrea ;
X = bruce ;
X = betty ;
X = doris ;
X = doris ;
X = clive ;
X = doris ;
X = clive ;
X = doris ;
X = clarissa ;
X = doris ;
X = clarissa ;
X = doris ;
false.
And then I change my definition of relative next way:
relative(X,Y) :- ancestor(X,Y).
relative(X,Y) :- ancestor(Y,X).
relative(X,Y) :- X\==Y, ancestor(Z,X), ancestor(Z,Y).
I simply change the order of goals in the last statement.
And now I have the following output:
relative(dave,X).
X = clive ;
X = clarissa ;
X = alan ;
X = andrea ;
X = bruce ;
X = betty ;
X = dave ;
X = doris ;
X = dave ;
X = doris ;
X = clive ;
X = dave ;
X = doris ;
X = clive ;
X = dave ;
X = doris ;
X = clarissa ;
X = dave ;
X = doris ;
X = clarissa ;
X = dave ;
X = doris ;
false.
I see dave in the output! How did this happen? I wrote that X \== Y... Can anybody give me a good explanation of this?
And one more question. How do I make my program not to write the same answers?
Thank you!
(\==)/2 is not a pure relation and can only be understood operationally. If you use it, exchanging the order of goals may yield declaratively wrong results:
?- X \== Y, X = Y.
X = Y.
Please use dif/2 instead for a pure and completely declarative way to state disequality of terms.
?- dif(X, Y), X = Y.
false.
See prolog-dif for more information.
Especially as a novice, try to abstain from using impure constructs and perserve logical-purity!
How? Use prolog-dif!
Instead of X \== Y simply write dif(X, Y).
Prolog is a programming language based on a specific resolution method, and the problem you describe is just that: a problem (well, I would call it a bug) in your program. The order of clauses and goals is how you control your algorithm: a sequence of steps with defined effects on your representation. Then a knowledge about such effects is IMHO unavoidable, and - I think - replacing (\==)/2 by dif/2 isn't going to make your life easier, when you'll try to code something more complex. At least, in my experience, I faced additional difficulties when I had to model and debug my code.
(\==)/2 is meant to ease metaprogramming, when you need to compare variables for identity. As such, it's effectively a rather advanced feature, not needed for your program. But (maybe because it's so similar to C/C++/Java operators), it's easy to undervalue its purpose.
For your usage, (\=)/2 would serve better, but again, it requires, for a simple use, that both arguments are instantiated. This being true, depends on the whole 'inference graph' resulting from the actual calling of goals - the operational semantic. Generally, it's not simple (or even feasible, I think) to determine if a predicate is safe to call with a specific pattern. Consider this counterexample I got as a comment about a my naive assertion - append/3, a pure Prolog library predicate, being safe for all instatiation patterns:
?- append(Xs,[a],Xs).
To avoid duplicates, and listing the outcome, I would use setof/3
?- setof(R, relative(dave, R), Relatives), maplist(writeln, Relatives).
Related
Using Prolog, I first created two facts called grade and food: The first fact is grade(X,Y) where X is the student (rob or matt) and Y is the grade level (freshman or sophomore). The second fact is food(X,Y) where X is the student (rob or matt) and Y is the food (pizza, burger, pasta, wrap).
I created a rule called preference(X,Y), where X is the student (rob or matt) and Y is the students' preference.
I want to enter preference(rob,X). in the GNU Prolog and have it return:
sophomore, pizza, burger.
However, it keeps returning: sophomore, pizza, pizza.
How do I fix this problem? I've spent hours looking into this. Thanks
This is the code I have:
grade(rob, sophomore).
grade(matt, freshman).
food(rob, pizza).
food(rob, burger).
food(matt, pasta).
food(matt, wrap).
preference(X,Y):-
grade(X,A),
food(X,B),
food(X,C),
Y = (A, B, C).
The way you have defined your facts is nice. The way you query it is not conventional. Here is how I would do it. The "preference" rule is simpler:
grade(rob, sophomore).
grade(matt, freshman).
food(rob, pizza).
food(rob, burger).
food(matt, pasta).
food(matt, wrap).
preference(X, A, Y):-
grade(X, A),
food(X, Y).
You conventionally query the database and get all solutions with backtracking:
?- preference(rob, Grade, Food).
Grade = sophomore,
Food = pizza ;
Grade = sophomore,
Food = burger.
If you want to collect the foods, you can use bagof/setof, like this:
?- bagof(Food, preference(rob, Grade, Food), Foods).
Grade = sophomore,
Foods = [pizza, burger].
What if you want to query all freshmen?
?- bagof(Food, preference(Person, freshman, Food), Foods).
Person = matt,
Foods = [pasta, wrap].
You need to state that the value of B and C are different; there are multiple ways to do that, for the simplicity I go with \==/2 (documentation):
preference(X,Y):-
grade(X,A),
food(X,B),
food(X,C),
B\==C,
Y = (A, B, C).
Gives the output
| ?- preference(X,Y).
X = rob
Y = (sophomore,pizza,burger) ? ;
X = rob
Y = (sophomore,burger,pizza) ? ;
X = matt
Y = (freshman,pasta,wrap) ? ;
X = matt
Y = (freshman,wrap,pasta) ? ;
no
If you don't want to have the basically doubled entries you can go with the (in this case lexical) "less than" #</2:
preference(X,Y):-
grade(X,A),
food(X,B),
food(X,C),
B #< C,
Y = (A, B, C).
| ?- preference(X,Y).
X = rob
Y = (sophomore,burger,pizza) ? ;
X = matt
Y = (freshman,pasta,wrap) ? ;
no
I may be wrong, but I suspect this may be a misunderstanding of prolog in general in addition to a non-intuitive REPL. Prolog doesn't really "return" a value, it just tries to match the variables to values that make your predicates true, and I would be willing to bet you're hitting enter after you see the first result.
The way preference is currently written B and C will match any two foods that rob is associated with. This could be pizza, pizza or pizza, burger or burger, pizza, or so on. It does not check whether B and C are equal. When I run preference(rob,X). prolog does not only give me the first result UNLESS I hit enter.
| ?- preference(rob,X).
X = (sophomore,pizza,pizza) ? ?
Action (; for next solution, a for all solutions, RET to stop) ?
If you hit a (or spam ; a few times) prolog will give you the rest of the results.
| ?- preference(rob,X).
X = (sophomore,pizza,pizza) ? a
X = (sophomore,pizza,burger)
X = (sophomore,burger,pizza)
X = (sophomore,burger,burger)
yes
| ?-
I think that all you really need to get all of a person's preferences is just food unless you specifically need them in a tuple or list which will take some slightly more complicated logic (let me know in a comment if that's what you're looking for)
| ?- food(rob, X).
X = pizza ? a
X = burger
yes
| ?-
Scenario
I have the code as below. My question is how to don't show appearing same result more than once.
male(charles).
male(andrew).
male(edward).
female(ann).
age(charles, 70).
age(ann, 65).
age(andrew, 60).
age(edward, 55).
nextking(X) :- age(X,P), age(Y,Q),
P>=Q, X\==Y; age(X,55).
Current Output
What I need
I need the output to be charles, ann, andrew, edward. No repetition of names.
With a fairly recent version, you can use library(solution_sequences):
?- distinct(nextking(X)).
X = charles ;
X = ann ;
X = andrew ;
X = edward.
or use the classic 'all solutions' builtin:
?- setof(K,K^nextking(K),Ks),member(X,Ks).
Ks = [andrew, ann, charles, edward],
X = andrew ;
Ks = [andrew, ann, charles, edward],
X = ann ;
...
but in this case, we loose the answer order defined by the KB.
Your nextking/1 predicate is rather inefficient, and furthermore does not guarantee the persons to be sorted by age.
If we would for example put charles last in the list of facts, we get:
?- nextking(X).
X = ann ;
X = ann ;
X = andrew ;
X = charles ;
X = charles ;
X = charles ;
X = edward.
basically te predicate you wrote has two clauses:
nextking(X) :-
age(X,P),
age(Y,Q),
P >= Q,
X\==Y.
nextking(X) :-
age(X, 55).
The first simply will yield any X for which there exists a person Y that is younger. But that thus gives no guarantees that these elements are sorted. Finally the last predicate will unify with all persons X that are 55 years old. For this specific case this works, but it would mean if we state another fact age(louise, 14), then this will fail. Not only is the approach incorrect, but even if it was correct it will be very "unstable".
We can make use of the setof/3 [swi-doc] predicate that does not only perform a uniqness filter, but also sorts the elements.
Since we want to sort the members of the royal family by descending age, we thus should construct 2-tuples (or an other structure that encapsulates the two parameters) where the first parameter contains the negative age, and the second parameter the corresponding person.
We can then use member/2 [swi-doc] to "unwind" the list in individual unifications:
nextking(X) :-
setof((NA, X), A^(age(X, A), NA is -A), Royals),
member((_, X), Royals).
This will produce the list of elements like:
?- nextking(X).
X = charles ;
X = ann ;
X = andrew ;
X = edward.
regardless how the facts are ordered in the source file.
I'm new to prolog so the concept of unifying is still new, so bear with me.
I want something like:
pair(X,Y)
That works like this:
?- pair(4,Y).
Y = 4,
?- pair(3,Y).
Y = 0,
But I don't really know how to do this. I know you can use something like X mod 2 =:= 0 which is true or false, but I don't know how to make "if this than that" code in prolog's way.
There are several ways to express a conditional statement in Prolog, besides using CLPFD (Constraint Logic Programming over Finite Domains):
1) make several statements, one for each case you are considering, and let Prolog execution mechanism find the one that is appropriate:
pair(X,Y) :- even(X), !, Y = X. % 'if X is even then Y = X'
pair(_,0). % 'else Y = 0'
In the second case here the unification is implicit, but in the first case it is explicit.
The _ symbol is a syntactic sugar for an 'anonymous variable', that is a variable for whose bindings we do not care and that we do not use.
The cut operator ! is used in the first clause to tell Prolog that it should not try to find other solutions for the Y if the first clause succeeds (like, backtracking and trying the second clause):
% without cut | % with cut
?- pair(4,Y). | ?- pair3(4,Y).
|
Y = 4 ? ; | Y = 4 ? ;
|
Y = 0 ? ; % <- wrong behaviour! | no.
|
no |
2) use the (Condition -> ThenStatement ; ElseStatement) if-then-else construct:
pair(X,Y) :- (even(X) -> Y = X ; Y = 0).
No cuts are allowed in the Condition argument. The first solution of the Condition is used.
3) some systems have the if/3 predicate, that is evaluated as if(Condition,ThenStatement,ElseStatement):
pair(X,Y) :- if( even(X), Y = X, Y = 0).
No cuts are allowed in the Condition argument.
I'm new to Prolog as I'm just starting to learn and write up my own small set of database rules. Using my own .pl file of database rules, I'm having a small problem with a query that I enter in Prolog, using these rules. Below shows my small database of rules:
staff(andy,18235,3).
staff(beth,19874,4).
staff(andy,18235,5).
staff(carl,16789,2).
staff(earl,34567,9).
sum([], 0).
sum([H|T], X) :-
sum(T, X1),
X is X1 + H.
getincome(Name, Income) :-
findall(Income,staff(Name,_,Income),Member),
sum(Member, Income).
As you can see, I have written a rule that finds the total income for a particular member of staff. This works very fine, as when I input:
?- getincome(andy, X).
The program always returns:
X = 8
As it should do, however whenever I instead input:
?- getincome(andy, 8).
This always returns false, when it should be true.
However when I also input:
?- getincome(andy, 3).
This returns true, due to already being in the database.
I'm just wondering, how could I modify this rule so that this could output true for the correct summation value, entered for any given staff (most particularly Andy), as opposed to the value already in the given database?
Ignore my question above!
Thanks for the help 'false'. I'm also having another issue, this time to do with working out and displaying the sum of the income for each member. I have modified my rules, in order to display this, as follows:
getincome(Name, I) :- staff(Name, _, _ ), findall(Income,staff(Name,_,Income),Member), sum(Member, I).
Whenever I enter the query:
?- getincome(X, Y).
I keep getting duplicate results of staff (most notably Andy, of course), as show below:
X = andy,
Y = 8 ;
X = beth,
Y = 4 ;
X = andy,
Y = 8 ;
X = carl,
Y = 2 ;
X = earl,
Y = 9.
What changes can I make to avoid these duplicates?
library(aggregate) offers a clean interface to solve such kind of problems:
?- aggregate(sum(S), K^staff(E,K,S), I).
E = andy,
I = 8 ;
E = beth,
I = 4 ;
E = carl,
I = 2 ;
E = earl,
I = 9.
One way to do this is to use bagof to collect each set of incomes:
person_income(Name, Income) :-
bagof(I, X^staff(Name,X,I), Incomes), % Incomes for a given name
sumlist(Incomes, Income). % Sum the incomes
With results:
| ?- person_income(Name, Income).
Income = 8
Name = andy ? a
Income = 4
Name = beth
Income = 2
Name = carl
Income = 9
Name = earl
yes
| ?- person_income(andy, Income).
Income = 8
yes
| ?- person_income(Name, 8).
Name = andy ? a
no
| ?-
I named this person_income to emphasize that it's a relation between a person and their income, rather than getincome which is more of an imperative notion, and doesn't really reflect that you can do more with the relation than just "get the income". Also, I'm using SWI Prolog's sumlist/2 here. GNU Prolog has sum_list/2. And as #CappeliC indicates in his answer, SWI Prolog has a handy aggregate predicate for operations like this.
I have written this little program:
married(guy1, fem1).
married(guy2, fem2).
married_to(X,Y):-!, married(X,Y); married(Y,X).
Prints:
X = guy1,
Y = fem1 ;
X = guy2,
Y = fem2.
My purpose was to print the married couples but one time each. The above rule works for me, but I can't understand why! How does this work?
What's the difference with that:
married_to(X,Y):- married(X,Y); married(Y,X).
This prints:
X = guy1,
Y = fem1 ;
X = guy2,
Y = fem2 ;
X = fem1,
Y = guy1 ;
X = fem2,
Y = guy2.
The cut commits execution to the first branch on the disjunction. Thus your rule:
married_to(X,Y):-!, married(X,Y); married(Y,X).
is operationally the same as:
married_to(X,Y):-!, married(X,Y).
And, given that there are no other clauses for the married_to /2 predicate, equivalent to:
married_to(X,Y):- married(X,Y).
imagine you have the equivalent definition married_to(X,Y):- true, !, married(X,Y) ; married(Y,X).. If true were to fail, then we'd jump to the alternative, but since it's not going to, we're firmly on the consequent branch (of the disjunction).
IOW your code is
married_to(X,Y):- ( !, married(X,Y) ) ; ( married(Y,X) ).
and not
married_to(X,Y):- !, ( married(X,Y) ; married(Y,X) ).
It is actually equivalent to
married_to(X,Y):- !, married(X,Y).
married_to(X,Y):- married(Y,X).
Perhaps with this it is easier to get a "feeling" for what is going on here. Obviously, the second clause of the predicate is cut off. :) We're committed to the first clause, the second doesn't get a chance to work at all.