Re: Meta Predicates and Variables in SWI Prolog

From: Bob Riemenschneider (rar_at_sdl.sri.com)
Date: 04/14/04


Date: 14 Apr 2004 13:22:36 -0700

Michael David Pedersen <mdp@cs.auc.dk> wrote in message news:<c4ee3i$3o2$2@sunsite.dk>...
> Hi Bart,
>
> Thank you for your prompt reply.
>
> > Michael David Pedersen wrote:
> > >
> > > However the list is being constructed at runtime and looks like
> > > ['A','B',
> > > '_']. I thus need to convert this to a list of atoms as needed in the
> > > apply predicate.
> > >
> > I would expect you to want to convert the list ['A', 'B', '_']
> > to a list of VARIABLES. No ?
>
> Right, I actually though that variables are atoms as well -- clearly, this
> is wrong (variables are terms).
>
> > Moreover, if you do
> >
> > ?- term_to_atom(X,'A'), term_to_atom(Y,'A').
> >
> > X and Y will be DIFFERENT variables and that might not be what you want
> > ...
>
> Right, this is not what I want.

Maybe this is too late to help -- I haven't been following the group
regularly of late and just saw your posts -- but I've appended some
code that you can simplify a bit and get what you want. The reason it
needs some simplification is
  (1) it works on on arbitrary terms, not just lists (so you can get
rid of
      subst/4 and subst_all/4, and stick to good ol' substitute/4),
and
  (2) it substitutes variables for only some of the atoms -- namely,
those that
      are arguments of metavar/1 or segment_metavar/1 -- so you don't
need to
      collect the list of atoms you want to want to substitute for

So, for example,

8 ?- subst_vars_for_metavar_names(
       f('D',g(metavar('A'),X),h(metavar('B'),metavar('A')),[metavar('C')],_,_),
       List,
       Subst).
    
X = _G996
List = f('D', g(metavar(_G1211), _G996), h(metavar(_G1385),
metavar(_G1211)), [metavar(_G1559)], _G1014, _G1015)
Subst = ['A'/_G1211, 'B'/_G1385, 'C'/_G1559]

Yes
9 ?-

                                        Regards,
                                        -- rar

%%%%
%%%% subst-vars.pl
%%%%
%%%% R. A. Riemenschneider (rar@sdl.sri.com)
%%%%
%%%% The term representation produced by the Acme+ parser is not quite
right
%%%% for use by the transformation engine. The problem has to do with
%%%% metavariables. Want the representation of, say, @x to be
metavar(X)
%%%% rather than metavar(x). Unfortunately, Prolog's (lack of)
facilities
%%%% for referring to variables when meta-programming makes this
impossible.
%%%% The best that can be done is to substitute the same fresh
variable
%%%% for every occurrence of 'x' when you're done, as below.
%%%%
%%%% Warning: The code is a might obscure.
%%%%

%% For SICSTUS
% :- use_module(library(lists)).

%% For SWI
remove_duplicates([], []).
      remove_duplicates([Head|Tail1], [Head|Tail2]) :-
      delete(Tail1, Head, Residue),
      remove_duplicates(Residue, Tail2).

subst_vars_for_metavar_names(InTerm, OutTerm, Subst) :-
        metavar_names(InTerm, NameBag, []),
        %% The following should be a win, because NameBag will be
        %% *much* smaller than InTerm in typical use.
        remove_duplicates(NameBag, NameSet),
        subst_vars_for_names(InTerm, NameSet, OutTerm, Subst).

%% Last two args are a "difference list"
metavar_names(Term, Names, RestNames) :-
        atomic(Term),
        !,
        Names = RestNames.
metavar_names(Term, Names, RestNames) :-
        var(Term),
        !,
        Names = RestNames.
metavar_names(metavar(X), [X|Names], Names) :- !.
metavar_names(segment_metavar(X), [X|Names], Names) :- !.
metavar_names(Term, Names, RestNames) :-
        % Term is neither atomic, a var, nor a compound with a "meta-"
functor
        Term =.. [_|Args],
        metavar_names_in_term_list(Args, Names, RestNames).

%% Last two args are a "difference list"
metavar_names_in_term_list([], Names, Names).
metavar_names_in_term_list([Arg|RestArgs], Names, RestNames) :-
        metavar_names(Arg, Names, RestArgNames),
        metavar_names_in_term_list(RestArgs, RestArgNames, RestNames).

subst_vars_for_names(Term, [], Term, []).
subst_vars_for_names(InTerm, [Name|Names], OutTerm,
[Name/FreshVar|Subst]) :-
        subst(Name, InTerm, FreshVar, Term),
        subst_vars_for_names(Term, Names, OutTerm, Subst).

%% Very similar to the substitute/4 library predicate -- basically,
%% it's substitute/4 generalized to terms other than lists and not
%% restricted to "top-level" matches -- so use the "same" arg order
subst(Name, InTerm, Var, OutTerm) :-
        Name == InTerm,
        !,
        OutTerm = Var.
subst(_, InTerm, _, OutTerm) :-
        var(InTerm),
        !,
        OutTerm = InTerm.
subst(_, InTerm, _, OutTerm) :-
        atomic(InTerm),
        !,
        InTerm = OutTerm.
subst(Name, InTerm, Var, OutTerm) :-
        InTerm =.. [F|InArgs],
        subst_all(Name, InArgs, Var, OutArgs),
        OutTerm =.. [F|OutArgs].

subst_all(_, [], _, []).
subst_all(Name, [InTerm|InTerms], Var, [OutTerm|OutTerms]) :-
        subst(Name, InTerm, Var, OutTerm),
        subst_all(Name, InTerms, Var, OutTerms).