Success and failure when querying the database - prolog

So I'm stuck on an exercise that I've been working on. I have the following facts:
sd(appleseed0, appleseed1).
sd(appleseed0, apple1).
sd(appleseed1, apple1).
sd(appleseed2, apple1).
sd(appleseed0, apple2).
sd(appleseed1, apple2).
sd(appleseed2, apple2).
What this means is that appleseed1 came from appleseed0, apple1 came from appleseed0, etc. The problem I'm having is that I need it to print out false if the values are switched around. Meaning, I want the query to result in "true" when the query is seed(appleseed0, apple1) and then "false" when the query is in opposite order like seed(apple1, appleseed0).
Right now, my predicate looks like this:
seed(A,B) :- sd(A,B) ; sd(B,A).
I understand that this is why my queries are returning true, no matter the order, but my only other idea is:
seed(A,B) :- sd(A,B).
but I cannot write it like that because that would make it an infinite loop with no false. How can I make it so that the query will result in "true" when shown with something like seed(appleseed2, apple2) and "false" when shown with something like seed(apple2, appleseed2)?

Hoping that I am reading your question correctly:
You don't need an extra predicate. Indeed, what you are looking for is the query:
?- sd(A, B).
This will succeed or fail just like you describe. The predicate
seed(A, B) :-
( sd(A, B)
; sd(B, A)
).
(just like yours, just formatted to be easier to follow) reads: "seed(A, B) is true when sd(A, B) is true. It is also true when sd(B, A) is true" (like you have noticed). An interesting side effect is that if you had these two facts in your database:
sd(foo, bar).
sd(bar, foo).
Then the query:
?- seed(foo, bar).
will succeed twice (!), just like the query
?- seed(bar, foo).
or the equivalent top level query
?- sd(bar, foo) ; sd(foo, bar).
That last query makes it most obvious why the query will succeed twice.
What confuses me: Why do you think that
seed(A, B) :-
sd(A, B).
will lead to an infinite loop? Is there some part of the program that you are not showing? As it stands, defining a predicate like this is equivalent to just giving sd/2 an alias, seed/2. This definition reads: "seed(A, B) is true when sd(A, B) is true."

Related

Is it possible to use ";" in a query in SWI-Prolog?

I'm new to Prolog and I'm now learning SWI-Prolog, I wonder if I can use ";" in a query to express disjunctive query conditions, e.g., I write this query
?-p(a,V1,V2),(V1=b;V1=null),(V2=c;V2=null)
and want to check if there exists a fact p/3, in which
the first term should be a constant "a";
the second term should either be a constant "b" or null;
the third term should either be a constant "c" or null.
I wonder if it's the right way to do the query and if there exists a more concise form like maybe
?-p(a,(b;null),(c;null))
Thanks!
Yes you can.
Writing the query
p(a,V1,V2),(V1=b;V1=null),(V2=c;V2=null).
after the ?- prompt is just a more direct way of adding the clause
myquery(V1,V2) :- p(a,V1,V2),(V1=b;V1=null),(V2=c;V2=null).
and then asking
?- myquery(V1,V2).
(myquery/2 being a newly minted and arbitrarily predicate here).
Incidentally, the clause
myquery(V1,V2) :- p(a,V1,V2),(V1=b;V1=null),(V2=c;V2=null).
could also be written "multiplied out", i.e.:
myquery :- p(a,b,c).
myquery :- p(a,b,null).
myquery :- p(a,null,c).
myquery :- p(a,null,null).

Prolog: Assign value to Variable in Predicate

With the following rules:
test('John', ebola).
test('John', covid).
test('Maria', covid).
How can I create a predicate that would tell me if John or Maria took (both) the Ebola and Covid tests?
I want to do something similar to this (I know it's wrong, just the idea):
tests(Persona, Ebola, Covid) :-
Ebola = test(Persona, ebola),
Covid = test(Persona, covid).
Prolog is relational not functional. test(X, Y) either holds or fails and doesn't return a value like what you thought. Here's what you should have written:
tests(Persona) :-
test(Persona, ebola),
test(Persona, covid).
You can query tests('John') which is true since both test/2 calls succeeds. The query tests('Maria') fails because test('Maria', ebola) fails.
Does it answer your question ?

Compile time testfor 'atoms'

Completely new to prolog. Interesting journey so far in trying to change how I think, so appreciate any help here.
I am trying to assert facts for a pre-defined set of names. For example, assume I have a a set of people [alice, bob, ...] in one file. I would like to assert facts about these folks in other files, but want to make sure that these folks exist and that is checked when the facts are loaded/compiled(?).
For example, assume I don't have 'chuck' in the list and I make an assertion:
user: swipl app.pl
?- full_name(chuck, "Charlie Steel").
should result in an error.
What is the best way I can do this?
So, here's the code I came up with:
person(deborah).
person(tony).
read_my_file(Filename) :-
open(Filename, read, In),
read_my_file1(In),
close(In).
read_my_file1(In) :-
read(In, Term),
( Term == end_of_file
-> true
; assert_or_abort(Term),
read_my_file1(In)
).
assert_or_abort(Term) :-
( full_name(Person, Name) = Term
-> ( person(Person)
-> assertz(full_name(Person, Name))
; format(user, '~w is not a person I recognize~n', [Person])
)
; format(user, '~w is not a term I know how to parse~n', [Term])
).
The trick here is using read/2 to obtain a Prolog term from the stream, and then doing some deterministic tests of it, hence the nested conditional structure inside assert_or_abort/1. Supposing you have an input file that looks like this:
full_name(deborah, 'Deborah Ismyname').
full_name(chuck, 'Charlie Steel').
full_name(this, has, too, many, arguments).
squant.
You get this output:
?- read_my_file('foo.txt').
chuck is not a person I recognize
full_name(this,has,too,many,arguments) is not a term I know how to parse
squant is not a term I know how to parse
true.
?- full_name(X,Y).
X = deborah,
Y = 'Deborah Ismyname'.

Prolog simple program

I am writing a little Prolog program, which is expected to do something as following:
?-input([allan,is,a,name]).
true.
?-input([Is,allan,a,name]).
true.
and here is my code:
% Simple answering agent
input(Text) :-
phrase(sentence(S), Text),
perform(S).
sentence(statement(S)) --> statement(S).
sentence(query(Q)) --> query(Q).
statement(Statement) -->
[Thing, 'is', 'a', Category],
{ Statement =.. [Category, Thing]}.
query(Query) -->
['Is', Thing, 'a', Category],
{ Query =.. [Category, Thing]}.
perform(statement(S)) :- asserta(S).
perform(query(Q)) :- Q.
the input([allan,is,a,name]). part seems working fine, but there is a trouble with the query part, for which if I type in input([Is,allan,a,name])., it prints
Is = 'Is'
Can someone please take a look at this problem for me, thank you.
Well, the problem is that Is is a variable and thus prolog instantiates it (with 'Is'). It would be good practise to ensure that all the members of the list are atoms but for a quick fix you could just do:
query(Query) -->
[_, Thing, 'a', Category],
{ Query =.. [Category, Thing]}.
this way, Is won't get instantiated and prolog will just say true. The only problem is that a statement could be interpreted as a query:
9 ?- input([allan, is, a, name]).
true ;
false.
10 ?- input([is, is, a, name]).
true .
11 ?- input([allan, is, a, name]).
true ;
true.
which can be fixed with some cuts (or be saying that Thing should be different than 'is' - if that's acceptable)
Edit: for a more general solution: it really depends on what kind of sentences you want to parse and what compromises the user can make. For example, it might be possible to ask him to give you words that are prolog atoms; if a word that starts with an upper case letter is requested he will have to use ''. Otherwise, it will be better to just give them in a string/atom ('Is allan a name' or "Is allan a name"). It's easy to separate it to individual atoms: use atomic_list_concat/3. For what is allan you still don't need to do something special; it's a 3 word sentence while the rest were 4 so you can separate it immediately.

Putting all results of a query in a list in Prolog

I'd like to know how to make a predicate that puts all results obtained from some query (so I get a result and press semicolon until I get False) in a list.
For example if I write foo(X,[1,2,3]). in some Prolog listener, let's say the result is
X=[11];
X=[22];
False.
I would like to get all those results in a list, so something like the following would happen.
?-another_foo(X,[1,2,3]).
X=[[11],[22]].
another_foo would somehow use foo to create a list with all the results from foo.
I just don't know how.
Use the built-in predicate findall/3:
?-findall(X0, foo(X0, [1,2,3]), X).
X = [[11], [22]].
You can define your another_foo/2:
another_foo(X, Input) :-
findall(X0, foo(X0, Input), X).

Resources