Converting Terms to Atoms preserving variable names in YAP prolog - prolog

Is there a way to configure YAP (and/or SWI prolog) so they will preserve variable names in any call to term_to_atom/2 ?.
For example, when I execute this:
term_to_atom(member(X, [1,2]), A).
I obtain this answer:
A = 'member(_131405,[1,2])'
Where X has been replaced by its internal representation.
However, I would like to get this answer instead:
A = 'member(X,[1,2])'
Thanks for any help!

There are two issues involved. How to get the variable name X into the system, and how to get a term with such a variable into the atom.
The X you type in is read by the top level which converts it to a regular variable which does not have a name associated. Let's see that in YAP:
?- read(Term).
|: X+3*Y+X.
Term = _A+3*_B+_A
The |: is YAP's prompt for input. And we have entered X+3*Y+X. However, the variable Term contains _A and _B (names chosen by the top level) in place of X and Y. So the information is lost and cannot be restored once it is read by read/1.
You have to access that information differently with the more general built-in for reading read_term/2,3 and the option variable_names/1.
?- read_term(T,[variable_names(Eqs)]).
|: X+3*Y+X.
Eqs = ['X'=_A,'Y'=_B],
T = _A+3*_B+_A
So the read option variable_names/1 gives you the information to restore the variable names. For each named variable read by read_term/2 there is a structure Name = Variable where Name is an atom representing the variable name. Above, 'X' is the name capital X.
Anonymous variables, that is variables whose name is _, do not occur in the list of variable names. They can be rapidly extracted like so:
?- read_term(T,[variable_names(Eqs)]),
term_variables(Eqs, Named),
term_variables(Named+T, Vars),
append(Named, Anons, Vars).
So much for the reading.
Now for the writing. We cannot write the term directly but have to accompany it with the list Eqs. Let's call the new predicate term_to_atom(Term, Eqs, Atom). In both YAP and SWI there is with_output_to(Output, Goal) which writes the output of Goal to different destinations like atom(A). So you can now use write_term/2 to write the term as you please. An example:
?- with_output_to(atom(A),write_term('a b'+X,[quoted(true)])).
A = '\'a b\'+_131284'.
The variable _131284 looks very ugly. To get variables associated with their names for printing we can implement term_to_atom/3 as follows:
term_to_atom(T, Eqs, A) :-
with_output_to(atom(A), write_term(T,[variable_names(Eqs),quoted(true)]) ).
And use it like so:
?- read_term(T,[variable_names(Eqs)]), term_to_atom(T, Eqs, Atom).
|: X+3*Y+X.
Atom = 'X+3*Y+X',
Eqs = ['X'=_A,'Y'=_B],
T = _A+3*_B+_A
variable_names/1 exists as a write option in ISO, Minerva, Jekejeke, GNU, B, SWI, YAP, and SICStus.
In SICStus, the originator of writing terms to lists, one writes:
:- use_module(library(codesio)).
term_to_atom(T, Eqs, Atom) :-
write_term_to_codes(T, Codes, [variable_names(Eqs),quoted(true)]),
atom_codes(Atom, Codes).
The following was an ISO incompatible work around for YAP prior to 6.3.4. It is no longer necessary. As for the differences to a separate write option: term_to_atom/3 as defined below interferes with constraints and does not correctly render '$VAR'/1.
But for the moment we can only approximate the ideal option
variable_names/1. To print terms with our own variable names,
variables have to be substituted in YAP by '$VAR'(Codes) where
Codes is a list of character codes. This does not do exactly the
same, but it is very close. This goes into a file:
:- use_module(library(apply)).
:- use_module(library(lambda)).
write_eqs_term(T, Eqs) :-
\+ \+ (
maplist(\Eq^( Eq = (N='$VAR'(Chs)), atom_codes(N,Chs)), Eqs),
write_term(T,[numbervars(true),quoted(true)])
).
term_to_atom(T, Eqs, A) :-
with_output_to(atom(A), write_eqs_term(T, Eqs) ).
For SWI, you would have to replace atom_codes(N,Chs) by N = Ch.
and install library(lambda) first. It's pre-installed in YAP.

Related

Representing truth regarding beliefs in prolog

How to make this (or something similar) work in Prolog:
belief(john,red(apple)).
belief(peter,red(apple)).
X :- belief(john,X), belief(peter,X).
And get true. for the following query (while consulting above):-
?- red(apple).
First, it's useful to define a little helper to capture when all (relevant) persons believe something:
all_believe(Belief) :-
belief(john, Belief),
belief(peter, Belief).
Then you can define, for example:
red(Object) :-
all_believe(red(Object)).
green(Object) :-
all_believe(green(Object)).
And with your given set of beliefs you get:
?- red(apple).
true.
?- green(apple).
false.
This works. It requires you to define similar rules for any term that you want to use as a belief.
You can make this a bit shorter with macro definitions using term_expansion:
term_expansion(declare_belief(Belief),
Belief :- all_believe(Belief)).
This means that every top-level definition in your source code of the form declare_belief(Belief) should be treated as if you had written Belief :- all_believe(Belief) instead (with the variable Belief substituted appropriately).
So now you can just write this:
declare_belief(red(_)).
declare_belief(green(_)).
and it will be treated exactly like the longer definitions for red(Object) and red(Object) above. You will still have to write this kind of declaration for any term that you want to use as a possible belief.
Prolog does not allow the head of a rule to be just a variable. The head must be a nonvar term, whose functor (i.e., name and arity) identifies the predicate being defined. So, a possible solution would be something like this:
true_belief(X) :-
belief(john, X),
belief(peter, X).
belief(john, red(apple)).
belief(peter, red(apple)).
Examples:
?- true_belief(red(apple)).
true.
?- true_belief(X).
X = red(apple).

Want PROLOG to require == matches on already matched variables, = for others

I have this code:
contradicts(at(X,_),location(X)).
mustContradict(A, B) :- contradicts(A, B).
contradicts/2 is meant to say: if X is at somewhere, then X can't itself be a location.
mustContradict/2 is meant to say: succeed if A and B are contradictory.
When I run it to detect that if you're at a location, you can't be a location (mustContradict(at(Thing,Location),location(Thing)).) -- it succeeds, as it should. On this one, however:
mustContradict(at(Thing,Location),location(Location)).
it also succeeds, with variable assignment Thing=Location.
I could probably mangle a way to make it ensure all variables are identical when trying to match, something like:
A=at(Thing,Location),B=location(Location),
contradicts(A,AsContradiction),
B==AsContradiction.
but then it would fail on the first test, trying to verify that a Thing that is "at" something can't be a location.
What I think I want is to be able to distinguish variables that are already assigned to other variables from those that are so far not matched to anything.
You shouldn't check your predicates with variables (starting with uppercase), but with terms (starting with lowercase).
mustContradict(at(thing,location),location(location)). fails as it should since thing doesn't equal location. In your example you use variables that can be assigned anything.
I have no idea why you are reassigning mustContradict(A, B) :- contradicts(A, B).
you can change contradicts definition to contradicts(at(X,Y),location(X)) :- not(X == Y).
I wonder if this is not what you wanted to achieve:
at(thing, location).
at(thingLocation, location).
location(location).
location(thingLocation).
contradicts(X) :- at(X,_), location(X).
now contradicts(X). succeeds with X = thingLocation
Looks like the solution was to convert all variables to atoms. Only converting those that were shared or repeated meant that the others could bind to inappropriate things (as in the first example), so the behavior didn't change.
Binding them to atoms, as in this code:
contradicts(at(X,_),location(X)).
mustContradict(X,Y) :-
replaceVariablesWithAtoms(X,NewX), replaceVariablesWithAtoms(Y,NewY),
contradicts(NewX,NewY).
replaceVariablesWithAtoms(Term,NewTerm) :-
Term =.. TermAsList,
terms_to_atoms(TermAsList,NewTermAsList),
NewTerm =..NewTermAsList.
terms_to_atoms([],[]).
terms_to_atoms([H|T],[NewH|NewT]) :-
terms_to_atoms(T,NewT),
term_to_atom(H,NewH).
gives the right answer to both queries.
?- mustContradict(at(Thing,Location),location(Thing)).
true.
?- mustContradict(at(Thing,Location),location(Location)).
false.
Wouldn't it be easier just to write it as you have defined the problem statement:
I have this code:
contradicts(at(X,_),location(X)).
mustContradict(A, B) :- contradicts(A, B).
contradicts/2 is meant to say: "if X is at somewhere, then X can't itself be a location."
mustContradict/2 is meant to say: "succeed if A and B are contradictory."
Assuming that your at/2 and location/1 are facts/predicates in your prolog program . . . why not something like this:
contradiction(X) :- at(X,_), location(X) .

How to get a listing of a specific knowledge base?

Suppose that the file foobar.pl in the current working
directory contains the following minimal knowledgebase:
foo(bar).
foo(baz).
frobozz.
If I start swi-prolog (by running swipl at the command), and immediately run
?- [foobar].
% foobar compiled 0.00 sec, 4 clauses
true.
?- listing.
...the contents of foobar are lost in a sea of >100 lines of unrelated output.
How can I limit listing's output to foobar?
Alternatively, how can I limit it to contents of those knowledgebases I have explicitly consulted?
I did look at the docs for listing/1 and listing/0, but I could not find anything helpful:
listing/1
List predicates specified by Pred. Pred may be a predicate name (atom), which lists all predicates with this name, regardless of their arity. It can also be a predicate indicator (/ or //), possibly qualified with a module. For example: ?- listing(lists:member/2)..
A listing is produced by enumerating the clauses of the predicate using clause/2 and printing each clause using portray_clause/1. This implies that the variable names are generated (A, B, ... ) and the layout is defined by rules in portray_clause/1.
listing/0
List all predicates from the calling module using listing/1. For example, ?- listing. lists clauses in the default user module and ?- lists:listing. lists the clauses in the module lists.
Of course, I did try the following useless idea:
?- foobar:listing.
true.
in SWI-Prolog, you can limit the scope of the loaded clauses with the module/2 directive. I.e. your file foobar.pl should become (for instance)
:- module(foobar, [foo/1]).
foo(bar).
foo(baz).
frobozz.
You can load the contents of a plain Prolog file into a module easily. For example:
?- fb:consult(foobar).
true
And then call:
?- fb:listing.
foo(bar).
foo(baz).
frobozz.
true.
Or list just a specific predicate:
?- fb:listing(foo/1).
foo(bar).
foo(baz).
true.

Arguments not sufficiently instantiated when consulting file

I'm running SWI-Prolog on a Mac through the Terminal. I'm trying to access an Atom file by writing the usual after opening up swipl in the terminal:
?- [hwk1-my_name].
Instead of swipl having the knowledge base to play with, it's giving me this:
ERROR: Arguments are not sufficiently instantiated
I'm new to Prolog, and my program as it stands now is simply the copied-and-pasted code provided by my professor to get the assignment started. Does this mean that the error is likely due to something within the code below, and if so, what is prompting this? Here is the code provided to me:
father(Dad, Child) :-
parent(Dad, Child),
male(Dad).
mother(Mom, Child) :-
parent(Mom, Child),
female(Mom).
had_a_child(Man, Woman) :-
father(Man, Child),
mother(Woman, Child).
sibling(Sibling1, Sibling2) :-
parent(Parent, Sibling1),
parent(Parent, Sibling2),
Sibling1 \= Sibling2.
brother(Brother, Sib) :-
sibling(Brother, Sib),
male(Brother).
sister(Sister, Sib) :-
sibling(Sister, Sib),
female(Sister).
Your obvious problem is the - inside the file name. The text editor you are using is completely irrelevant. Even confusing, as one of Prolog's data types is the atom.
You have two options:
Use file names that would be valid Prolog atoms even without quoting. This means that they cannot start with a capital or a digit, and can contain only letters, digits, and underscores (_). Then, your file can still have the .pl extension and you can consult it like you do: foo.pl ---> ?- [foo].
Use the complete filename, extension included, and put single quotes around it: foo-bar.baz ---> ?- ['foo-bar.baz'].. As you will see, you don't even need the .pl extension any more.
Whenever you are in doubt about what Prolog sees, you can try write_canonical/1:
?- write_canonical(hwk1-my_name).
-(hwk1, my_name)
true.
In other words, Prolog takes this as the compound term -/2 with the atoms hwk1 and my_name as the first and second argument.

Not in Prolog and use of Bagof

I have database like this:
movie(matrix,wachowski,thriller).
movie(terminator, cameron, thriller).
movie(Gladiator, scott, costume).
movie(star wars, lucas, fantasy).
movie(star trek, abrams, fantasy).
And I want to know who direct fantasy film except Abrams.
I suppose I need to use 'not' predicate, but I don't know exactly how it works.
?- movie(X,not(abrams),fantasy).
But unfortunately it doesn't work.
One more query is what kind of films is not a thriller:
?- movie(X,_,not(thriller)).
Still not working.
Next problem is I need to use predicate direct(Director, listsOfMovie) based on bagof.
?- direct(Director, listsOfMovie) :- bagof(Director,movie(Director,listsOfMovie,_), listsOfMovie).
Still without success :(
Anyone can help?
Use of not
You can't use Prolog predicates like functions. not/1 is a predicate which accepts a query as an argument. So this isn't doing what you think:
movie(X,not(abrams),fantasy).
This is querying movie with a second argument of not(abrams). You don't have any facts or predicates that match movie(_, not(_), _) so it will always fail.
If you want to know which films were not thrillers, you might render it:
movie(X, _, Type),
Type \= thriller.`
Using not, it might be:
not( movie(X, _, thriller) ).
If you wanted the syntax of movie(_, not(_), _) to work, you could write a predicate for it:
movie( Name, not(Director), Type ) :-
movie(Name, D, Type),
D \= Director.
Now we have either a fact or a predicate head that matches the form, movie(_, not(_), _), and then the query, movie(X, not(abrams), Y) would work. But it's not normally done this way.
Using bagof/3
Let's look at your use of bagof. In the simplest case, bagof is supposed to take three arguments:
bagof(X, {query involving X}, ListOfSatisfingXs)
So bagof will run the {query involving X} generating each X that makes it true, creating ListOfSatisfingXs, a unique, sorted list of such instantiations of X. In other words, ListOfSatisfingXs is the unique, sorted values of X that make {query involving X} succeed.
In your case, you've gotten the arguments to bagof a bit mixed up:
direct(Director, listsOfMovie) :-
bagof(Director, movie(Director, listsOfMovie, _), listsOfMovie).
Here, you're reusing your Director argument as your bagof argument, which is not good (since it's not intended). Since you're looking for a list of movies, the first argument should represent the movie. Your query to movie is using listsOfMovie, your intended target argument to hold the list result, which it shouldn't. And finally, listsOfMovie is an atom, not a variable, since it doesn't start with a capital letter.
The corrected version would be:
director_movies(Director, ListOfMovies) :-
bagof(Movie, movie(Director, Movie, _), ListOfMovies).
Here, the bagof is getting the *Unique, sorted list of Movie values such that movie(Director, Movie, _) is true and providing that resulting list in ListOfMovies.

Resources