user797257
user797257

Reputation:

State facts with unbound variables

How would I state things "in general" about the facts? Suppose I need to state "everyone likes the person who likes him/her", and I have a list of people who may or may not like each other.

This is what I tried so far, but it's sure not the way to do it:

likes(dana, cody).
hates(bess, dana).
hates(cody, abby).

likes(first(Girl, OtherGirl), first(OtherGirl, Girl)).
hates(Girl, OtherGirl):- \+ likes(Girl, OtherGirl).

because this won't even compile.


everybody([dana, cody, bess, abby]).

likes_reflexive(dana, cody).
hates(bess, dana).
hates(cody, abby).
likes_reflexive(X, Y):- likes(X, Y), likes(Y, X).

hates(Girl, OtherGirl):- \+ likes(Girl, OtherGirl).

%% likes_reflikes_reflexive(X, Y):- likes(X, Y), likes(Y, X).
%% user:6: warning: discontiguous predicate likes_reflexive/2 - clause ignored

%% hates(Girhates(Girl, OtherGirl):- \+ likes(Girl, OtherGirl).
%% user:8: warning: discontiguous predicate hates/2 - clause ignored

Unfortunately I don't understand what the warnings say. Hope it makes my intention more clear. I.e. by stating one fact, I also want to state the other related fact.

Upvotes: 0

Views: 292

Answers (3)

Cephalopod
Cephalopod

Reputation: 15145

What you want is a fact where Prolog does not care about the order of arguments. Alas, something like that does not exist. What you can do instead is define facts where the implied meaning is that it is valid for all argument orders (like_each in the example below). But of course, you cannot use these facts in that way. Instead, you define the actual predicate to try (hence the or ;) all possible argument orders.

Thus, the solution is:

%% bi-directional like
like_each(dana, cody).

likes(A, B) :- like_each(A, B); like_each(B, A).

%% optional: one-directional like
% likes(cody, sarah).

Also, be careful with

hates(Girl, OtherGirl):- \+ likes(Girl, OtherGirl).

If both variables are unbound (e.g., ?- hates(A,B)), it will always fail. This happens because Prolog first tries to find a match for likes, which always succeeds for two variables, and then negates the result. Thus, you cannot use hates to find all pairs who don't like each other.

Upvotes: 0

zaquest
zaquest

Reputation: 2050

If you want to change your knowledge base dynamically, you can use asserts. If you want to modify existing predicate, you should define it as dynamic, e.g. :- dynamic(likes/2).. If predicate is undefined, you can omit it.

add_mutual_likes(X, Y) :- asserta(likes(X, Y)), asserta(likes(Y, X)).

:- initialization(add_mutual_likes(dana, cody)).

initialization/1 calls add_mutual_likes(data, cody) goal when file is loaded. add_mutual_likes/2 adds two facts to a database. asserta/1 converts it's argument into a clause and adds it to a database.

| ?- [my].
yes
| ?- listing(likes/2).
% file: user_input

likes(cody, dana).
likes(dana, cody).

yes
| ?- likes(cody, dana).
yes
| ?- likes(dana, cody).
yes

| ?- add_mutual_likes(oleg, semen).
yes
| ?- listing(likes/2).
% file: user_input

likes(semen, oleg).
likes(oleg, semen).
likes(cody, data).
likes(data, cody).

yes

I use gprolog.

Upvotes: 1

NotAUser
NotAUser

Reputation: 1466

Let's start with the warnings. They are merely "style" suggestions. They are telling you that all the definitions for likes and hates should be together. Trust me if you have a big Prolog program it becomes a nightmare to go around tour code to get the full definition of your predicate. It would be like writing half a function in C++ and finish it in another file.

Now, you want to say "everyone likes the person who likes him/her". I'm not sure why you are using that function "first" in the code. This would be sufficient:

likes(dana, cody).
likes(Girl, OtherGirl) :- likes(OtherGirl, Girl).

The second clause reads "Girl likes OtherGirl if OtherGirl likes Girl. This won't work. If you ask your program "is it true that cody likes dana"

? likes(cody, dana)

Prolog will reason like this:

  • The answer is yes if dana likes cody (using the second clause).
  • Yes! Because dana likes cody (using the first clause).

This is not enough to make it a correct program. Since we are in Prolog you can say: "give me another solution" (usually by entering ";" in the prompt). Prolog will think "I only used the first clause, I haven't tried the second".

  • The answer is Yes also if dana likes cody (using the second clause).
  • The answer is Yes according to the second clause, if cody likes dana.

But that's our initial query. Prolog will give you the same answer again and again, looping forever if you asked for all the solutions.

You can do two things here. The first is telling Prolog that one solution is enough. You do this adding a "!" (that basically says, clear all the open branches left to explore).

likes(dana, cody) :- !.
likes(Girl, OtherGirl) :- likes(OtherGirl, Girl).

Another alternative is to "stratify the program".

direct_likes(dana, cody).
likes(Girl, OtherGirl) :- direct_likes(OtherGirl, Girl), !.
likes(Girl, OtherGirl) :- direct_likes(Girl, OtherGirl).

Upvotes: 0

Related Questions