Related
SWI-prolog version: threaded, 64 bits, version 7.6.4
OS: Ubuntu bionic 18.04
Still working on learning prolog and ran into an interesting situation. I would like to use a predicate that is 'asserted' over multiple files (just makes the code look a little cleaner in organization), but that predicate also needs to be able to process rdf prefixes as part of the semweb package and it does not seem to work.
Here is a code block that might show the problem:
:- module(multifile1,
[
test/2
]
).
:- use_module(library(semweb/rdf_db)).
:- use_module(library(semweb/rdfs)).
:- multifile
multifile1:bar/1,
multifile1:foo/1.
:- rdf_meta
test(-, r),
foo(r).
test(bar, X) :-
bar(X).
test(foo, X) :-
foo(X).
multifile1:bar(abc) :-
format('bar: abc~n', []).
foo(rdf:about) :-
format('foo: rdf:about~n', []).
And asserting the same foo in another file:
:- module(multifile2,
[
]
).
:- use_module(library(semweb/rdf_db)).
:- use_module(library(semweb/rdfs)).
:- multifile
multifile1:foo/1.
:- rdf_meta
multifile1:foo(r).
multifile1:foo(rdf:type) :-
format('rdf:type~n', []).
In this form calling test(foo, rdf:about) works, but asserting test(foo, rdf:type) does not work. Is rdf_meta and multifile not supposed to work together or there is a bug in this code?
PS: I had added the multifile bar to make sure that works over multiple files.
I think I figured out. multifile and rdf_meta do work together. The bug in the code was related to the fact that I had defined foo in rdf_meta as foo(r). I think that expansion was causing confusion at compile time with regards to pattern match. Changing the definition to foo(-) fixed the problem. Hope this helps someone in the future.
I decided to start playing with Prolog (SWI-Prolog). I wrote a program, and now I'm trying to write a simple main predicate so that I can create a .exe and run the program from the command line. That way, I can find true/false relationships from the command line rather than from the prolog GUI. However, I am having trouble understanding what actually goes in the main predicate. Here is the program so far:
mother(tim, anna).
mother(anna, fanny).
mother(daniel, fanny).
mother(celine, gertrude).
father(tim, bernd).
father(anna, ephraim).
father(daniel, ephraim).
father(celine, daniel).
parent(X,Y) :- mother(X,Y).
parent(X,Y) :- father(X,Y).
ancestor(X, Y) :- parent(X, Y).
ancestor(X, Y) :- parent(X, Z), ancestor(Z, Y).
First Try:
I put all of the relationship definitions in a predicate called family_func().Then, I tried to call that function from main by typing main. into the command line. I expected to be able to start finding relationships like I did before I created the predicates, but instead, the program started with this error:
ERROR: c:/.../ancestor.pl:18:0: Syntax error: Operator expected
Here is the code:
family_func():-
mother(tim, anna).
...
parent(X,Y) :- mother(X,Y).
...
main:-
family_func().
Second Try:
I tried putting all of the definitions in the main predicate. I expected to be able to type main. and then have the program pause and wait for me to start entering clauses (almost like in Java where you run a program and it waits for user input). Instead, when I typed main., it returned false.
Question 1:
I am used to writing code in Java. So, in my mind, The first thing I tried should work. I basically defined local variables in family_func() and then I called smaller "methods" (i.e. parent(X,Y) :- mother(X,Y).) that should find the relationships between those variables. When I call main, at the very least, I would have expected the program to wait for me to enter the relationship, return the result, and then close. Why doesn't this work?
Question 2:
How would I actually write a main predicate? Are there any good examples for a program like this? I've tried the example here, but couldn't get it to work.
Thanks for any help.
Edit:
New Attempt - main. returns false, and running parent(tim, anna). right after running main. returns false even though it should be true.
:- dynamic mother/2.
:- dynamic father/2.
family_func:-
assert(mother(tim, anna)).
assert(father(tim, bernd)).
parent(X,Y) :- mother(X,Y).
parent(X,Y) :- father(X,Y).
ancestor(X, Y) :- parent(X, Y).
ancestor(X, Y) :- parent(X, Z), ancestor(Z, Y).
main:-
family_func.
Edit:
Just in case anyone else needs to know, as #CapelliC states in the comments under the answer, there needs to be a comma between calls. For example:
family_func:-
assert(mother(tim, anna)),
assert(mother(anna, fanny)),
assert(mother(daniel, fanny)),
assert(mother(celine, gertrude)),
assert(father(tim, bernd)),
assert(father(anna, ephraim)),
assert(father(daniel, ephraim)),
assert(father(celine, daniel)).
I think should be (no empty argument list allowed)
:- dynamic mother/2.
... other dynamically defined relations...
family_func:-
assert(mother(tim, anna)).
...
% rules can be dynamic as well, it depends on your application flow...
parent(X,Y) :- mother(X,Y).
...
main:-
family_func.
I'm currently trying to learn how to use Prolog. I have SWI-Prolog version 6.2.6 installed.
It seems to run:
?- 3<4.
true.
?- 4<3.
false.
As a first example, I was trying to implement the possibility of asking questions about a family tree. So I've started with this, stored in family.pl:
father(bob,danna).
father(bob,fabienne).
father(bob,gabrielle).
mother(alice,danna).
mother(alice,fabienne).
mother(alice,gabrielle).
father(charlie,ida).
father(charlie,jake).
mother(danna,ida).
mother(danna,jake).
father(edgar,kahlan).
mother(fabienne,kahlan).
father(hager,luci).
mother(gabrielle,luci).
male(X) :- father(X,_).
female(X) :- mother(X,_).
But when I try to load this with consult(family). I get:
?- consult(family).
Warning: /home/moose/Desktop/family.pl:7:
Clauses of father/2 are not together in the source-file
Warning: /home/moose/Desktop/family.pl:9:
Clauses of mother/2 are not together in the source-file
Warning: /home/moose/Desktop/family.pl:11:
Clauses of father/2 are not together in the source-file
Warning: /home/moose/Desktop/family.pl:12:
Clauses of mother/2 are not together in the source-file
Warning: /home/moose/Desktop/family.pl:13:
Clauses of father/2 are not together in the source-file
Warning: /home/moose/Desktop/family.pl:14:
Clauses of mother/2 are not together in the source-file
% family compiled 0.00 sec, 17 clauses
true.
I don't understand what is the problem here. I've found some results that mentioned that - cannot be used in identifiers, but I didn't use - in an identifier.
Question 1: What causes the Warning from above? How can I fix it?
But there are only warnings, so I've continued with
?- female(fabienne).
true.
?- male(fabienne).
false.
Ok, this seems to work as expected.
Then I've added
male(jake).
female(ida).
female(kahlan).
female(luci).
brother(X,Y):-
male(X),
(mother(Z,X)=mother(Z,Y);father(Z,X)=father(Z,Y)).
and tried:
?- brother(jake,ida).
false.
Why isn't this true?
Question 2: What is the problem with my brother rule?
Your first question is answered here.
As for the second, you're thinking in terms of functions instead of relations.
mother(Z,X) = mother(Z,Y)
is the same as saying X = Y because it compares two terms, without interpreting them. If you want Z to be the mother of both X and Y, you need a conjunction:
mother(Z, X), mother(Z, Y)
the warrning can be fixed if you move all the father and mother declarations together (do not mix them)
In brother definition you use = and you should use a logical AND so use ,(comma)
also you need to tell prolog that jake is a male because he can't figure it out from those rules
I split the brother definition in 2 because is more clear and this is like a logical OR (the first definition is valid or the second one)
father(bob,danna).
father(bob,fabienne).
father(bob,gabrielle).
father(charlie,ida).
father(charlie,jake).
father(edgar,kahlan).
father(hager,luci).
mother(alice,danna).
mother(alice,fabienne).
mother(alice,gabrielle).
mother(danna,ida).
mother(danna,jake).
mother(fabienne,kahlan).
mother(gabrielle,luci).
male(jake).
male(X) :- father(X,_).
female(X) :- mother(X,_).
brother(X,Y):-
male(X),
mother(Z,X),mother(Z,Y).
brother(X,Y):-
male(X),
father(Z,X),father(Z,Y).
For test run
brother(X,Y).
for more results you need to add who is male opr female for those childs like I did for jake
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.
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.
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).
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.
I'm having a lot of fun using Logtalk, but ran into an issue using phrase_from_file. Specifically, my case looks something like this:
:- object(scan_parser).
:- public(scanlist//1).
scanlist([Scan|Scans]) --> scan(Scan), dcg_basics:blanks, scanlist(Scans).
scanlist([]) --> [].
:- public(scan_file/2).
:- mode(scan_file(+filename, -scans), one).
scan_file(Filename, Scans) :- pio:phrase_from_file(scanlist(Scans), Filename).
...
:- end_object.
The trouble is all in that call to phrase_from_file. It's unable to find scanlist, presumably because it is local to this object, so I get this error:
?- scan_parser::scan_file('input.txt', Scans).
ERROR: phrase/3: Undefined procedure: pio:scanlist/3
But, if I try to aggrandize it with a module reference like so:
scan_file(Filename, Scans) :- pio:phrase_from_file(::scanlist(Scans), Filename).
I get this error:
?- scan_parser::scan_file('input.txt', Scans).
ERROR: phrase/3: Undefined procedure: pio: (::)/3
Same if I use pio:phrase_from_file(this::scanlist(Scans), Filename) or pio:phrase_from_file(scan_parser::scanlist(Scans), Filename). If I use a single colon instead in emulation of SWI's module facility, I get messages like ERROR: phrase/3: Undefined procedure: scan_parser:scanlist/3.
I assume that the problem here is that SWI's PIO library is trying to construct something to hand to phrase and it's just not intelligent enough. But this is something that comes up for me a lot, using phrase_from_file/2, and I'm sure there will be other times I want to excavate something from SWI's library and borrow it. What's the right way forward? I'd like to preserve the encapsulation of Logtalk as much as possible.
Thanks!
I'm designing a general solution for Logtalk 3.x to support Prolog module meta-predicates that take closures as meta-arguments. Meanwhile, can you try the following (ugly) workaround:
% ensure the module is loaded
:- use_module(library(pio)).
:- object(scan_parser).
% override the non-standard meta-arguments declarations
:- meta_predicate(pio:phrase_from_file(2,*)).
:- public(scanlist//1).
scanlist([Scan|Scans]) --> scan(Scan), dcg_basics:blanks, scanlist(Scans).
scanlist([]) --> [].
:- public(scan_file/2).
:- mode(scan_file(+filename, -scans), one).
scan_file(Filename, Scans) :- pio:phrase_from_file(user:scan_parser_scanlist(Scans), Filename).
{scan_parser_scanlist(Scans, A, B)} :-
phrase(scanlist(Scans), A, B).
...
:- end_object.
I cannot test as you only posted part of the object code.