Is it possible to preserve variable names when writing and reading term programatically?
Asked Answered
R

2

6

I'm trying to write an SWI-Prolog predicate that applies numbervars/3 to a term's anonymous variables but preserves the user-supplied names of its non-anonymous variables. I eventually plan on adding some kind of hook to term_expansion (or something like that).

Example of desired output:

    ?- TestList=[X,Y,Z,_,_].
       > TestList=[X,Y,Z,A,B].

This answer to the question Converting Terms to Atoms preserving variable names in YAP prolog shows how to use read_term to obtain as atoms the names of the variables used in a term. This list (in the form [X='X',Y='Y',...]) does not contain the anonymous variables, unlike the variable list obtained by term_variables, making isolation of the anonymous variables fairly straightforward.

However, the usefulness of this great feature is somewhat limited if it can only be applied to terms read directly from the terminal. I noticed that all of the examples in the answer involve direct user input of the term. Is it possible to get (as atoms) the variable names for terms that are not obtained through direct user input? That is, is there some way to 'write' a term (preserving variable names) to some invisible stream and then 'read' it as if it were input from the terminal?

Alternatively... Perhaps this is more of a LaTeX-ish line of thinking, but is there some way to "wrap" variables inside single quotes (thereby atom-ifying them) before Prolog expands/tries to unify them as variables, with the end result that they're treated as atoms that start with uppercase letters rather than as variables?

Rorry answered 24/1, 2019 at 22:28 Comment(3)
write_term is not good enough?Seanseana
You can use read_term/3 supplying a stream in the first position if you want to read terms from somewhere besides the terminal. Can you say more about why you need this functionality? It may be that the real problem you're trying to solve can be approached more directly from another angle, if we know more about what you're trying to do.Gainly
Oops, I linked to the wrong question and answer. I just corrected it. Sorry if that caused any confusionRorry
M
3

You can use the ISO core standard variable_names/1 read and write option. Here is some example code, that replaces anonymous variables in a variable name mapping:

% replace_anon(+Map, +Map, -Map)
replace_anon([_=V|M], S, ['_'=V|N]) :- member(_=W, S), W==V, !, 
   replace_anon(M, S, N).
replace_anon([A=V|M], S, [A=V|N]) :- 
   replace_anon(M, S, N).
replace_anon([], _, []). 

variable_names/1 is ISO core standard. It was always a read option. It then became a write option as well. See also: https://www.complang.tuwien.ac.at/ulrich/iso-prolog/WDCor3

Here is an example run:

Welcome to SWI-Prolog (threaded, 64 bits, version 7.7.25)

?- read_term(X,[variable_names(M),singletons(S)]), 
   replace_anon(M,S,N), 
   write_term(X,[variable_names(N)]).
|: p(X,Y,X).
p(X,_,X)

To use the old numbervars/3 is not recommended, since its not compatible with attribute variables. You cannot use it for example in the presence of CLP(FD).

Medford answered 26/1, 2019 at 17:51 Comment(0)
M
1

Is it possible to get (as atoms) the variable names for terms that are not obtained through direct user input?

if you want to get variable names from source files you should read them from there.

The easiest way to do so using term expansion.

Solution:

read_term_from_atom(+Atom, -Term, +Options)

Use read_term/3 to read the next term from Atom.

Atom is either an atom or a string object.

It is not required for Atom to end with a full-stop.

Use Atom as input to read_term/2 using the option variable_names and return the read term in Term and the variable bindings in variable_names(Bindings).

Bindings is a list of Name = Var couples, thus providing access to the actual variable names. See also read_term/2.

If Atom has no valid syntax, a syntax_error exception is raised.

write_term( Term ) :-
  numbervars(Term, 0, End),
  write_canonical(Term), nl.
Master answered 25/1, 2019 at 6:28 Comment(2)
Thanks, this works perfectly when working from an atom. But what if the term I want the variable names from isn't an atom, but (for example) a compound functor, a list, or a standalone variable? These can all be handled with read_term and direct user input, as the answer I linked demonstrates... but is there some way to directly redirect the output of a write_term to read_term, etc, without parsing the variables as variables / without replacing them with anons? I understand how to use read_term_from_atom, but I'm looking for a sort of read_term_from_term or read_term_verbatim...Rorry
possibly you simply need portray_clause/1. This pretty-prints terms assigning variable names starting from A, B... swi-prolog.org/pldoc/man?predicate=portray_clause/2Master

© 2022 - 2024 — McMap. All rights reserved.