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.