Replace atom with variable - prolog

I have a term which may or may not contain the atom 'this'. The term may also contain variables.
I need to replace 'this' with a variable I. How can I do this?
I tried to do something like this:
term_to_atom((f(a), g(this, b), ...), A),
tokenize_atom(A, L),
replace(this, I, L, L2)
It seemed to work. The problem is, I need to go back to the original term and I can't do it...

SWI-Prolog has atomic_list_concat/2 and atom_to_term/2 which should help you go back to the original term.
main :-
term_to_atom((f(a), g(this, b)), A),
tokenize_atom(A, L),
replace(this, 'I', L, L2),
atomic_list_concat(L2, A2),
atom_to_term(A2, T, []),
writeln(T).
?- main.
f(a),g(_G69,b)
true .

Take a look at this predicate (replace/4):
replace(Term,Term,With,With) :-
!.
replace(Term,Find,Replacement,Result) :-
Term =.. [Functor|Args],
replace_args(Args,Find,Replacement,ReplacedArgs),
Result =.. [Functor|ReplacedArgs].
replace_args([],_,_,[]).
replace_args([Arg|Rest],Find,Replacement,[ReplacedArg|ReplacedRest]) :-
replace(Arg,Find,Replacement,ReplacedArg),
replace_args(Rest,Find,Replacement,ReplacedRest).
An example of what you need:
| ?- replace(f(1,23,h(5,this)),this,Var,Result).
Result = f(1,23,h(5,Var))
yes

Related

How to link constant with variable by assert?

I want to add in the DB a constant and a linked variable:
?- assertz(my(x, A))
So that in the future I can define A and get the only one entry. Sth like that:
?- assertz(my(x, A)), ..., A = 2.
?- my(A, B).
A = x,
B = 2.
Can this be done?
As I noted in the comments your idea of a link like a pointer is not the way to approach solving your problem.
A common solution is to walk the tree and construct a new tree as you walk the tree by replacing the leaf of the tree with a new leaf that contains the value from the input tree along with the associated value, what you are thinking should be linked.
Since you are somewhat new to Prolog I will do this with two examples. The first will just walk a tree and only return true when successfully walked. It can be used to understand how to walk a tree and run with gtrace to single step the code to understand it.
The second example will expand on the tree walk and add the type (link as you think) to the leaf item. The the old leaf for something simple like an atom a, will become a new leaf in the tree like (a,atom).
Also this was quickly written as a demonstration only. I am sure it will have problems if pressed into doing anything more than the single example.
:- module(example,
[
example/1
]).
example(walk) :-
Term = term_size(a(1,"Hello",'Atom',1+2,[a,$,T])),
walk(Term).
example(infer_type) :-
Term = term_size(a(1,"Hello",'Atom',1+2,[a,$,T])),
infer_type(Term,Is),
write(Is).
walk([]) :- !.
walk([T]) :- var(T), !.
walk(L) :- is_list(L), !, L = [H|T], walk(H), walk(T).
walk(T) :- compound(T), !, T =.. [_|Args], !, walk(Args).
walk(T) :- integer(T), !.
walk(T) :- var(T), !.
walk(T) :- atomic(T), !.
walk(T) :- T =.. [Arg|Args], !, walk(Arg), walk(Args).
infer_type([],[]) :- !.
infer_type([T],[(T,var)]) :- var(T), !.
infer_type(L,S) :- is_list(L), !, L = [H|T], infer_type(H,I), infer_type(T,Is), S = [I|Is].
infer_type(T,S) :- compound(T), !, T =.. [F|Args], !, infer_type(Args,Is), S =.. [F|Is].
infer_type(T,(T,integer)) :- integer(T), !.
infer_type(T,(T,var)) :- var(T), !.
infer_type(T,(T,atom)) :- atomic(T), !.
infer_type(T,S) :- T =.. [Arg|Args], !, infer_type(Arg,I), infer_type(Args,Is), S =.. [I|Is].
Example run
Note: I know there are warnings; it is a demo not production code.
Welcome to SWI-Prolog (threaded, 64 bits, version 8.5.3)
?- working_directory(_,'C:/Users/Groot').
true.
?- [example].
Warning: c:/users/Groot/example.pl:20:
Warning: Singleton variables: [T]
Warning: c:/users/Groot/example.pl:24:
Warning: Singleton variables: [T]
true.
?- example(walk).
true.
?- example(infer_type).
term_size(a((1,integer),(Hello,atom),(Atom,atom),(1,integer)+(2,integer),[(a,atom),(($),atom),(_25642,var)]))
true.
As an exercise I did not identify the string as a string, the change should be easy.

How to use the "-" constructor in Prolog?

So I need to create a Prolog predicate that takes an input that looks like this [true-X, false-Y, false-X, true-Z] and only return the variables that occur once. So for this example, it would return [true-Z] since Z only occurs once. I have been able to do this with just normal lists.
singles([],[]).
singles([H | T], L) :-
member(H, T),
delete(T, H, Y),
singles( Y, L).
singles([H | T], [H|T1]) :-
\+member(H, T),
singles(T, T1).
If I run this then it returns
?- singles([1,1,2,3,4,3,3,2], R).
R = [4]
since it only returns the values that appear once in the list. The problem with what I'm trying to do is that I can't use the member or delete predicates with the "-" constructor. Basically, I have to start by splitting each item into it's two parts and then just compare the variable singles([Pol-Var | T], L). To compare the two variables, I created an occurs predicate that compares the variable at the head of the list.
occurs(X, [Pol-Var|T]) :- X == Var.
Here's what I have so far.
singles([],[]).
singles([Pol-Var | T], L) :-
occurs(Var, T),
singles(T, L).
singles([Pol-Var | T], [Pol-Var|T1]) :-
\+occurs(Var, T),
singles(T, T1).
occurs(X, [Pol-Var|T]) :- X == Var.
What this does is basically like if I had the input [1,1,2,3,2] then the output would be [1,2,3,2] so it just removes any duplicates that are right beside eachother. So if I had the input [true-X, false-X, false-Y, true-Y, true-Z] then the output would be [false-X, true-Y, true-Z] and I want it to be [true-Z]. How can I do that?
As Daniel pointed out in his first comment, the real problem you're facing is the unwanted unification performed by Prolog between the arguments of such builtins like member/2 or delete/3. An old trick-of-the-trade of the Prolog community is to use double negation to achieve matching without unification, but as we'll see, this would not help you too much.
The simpler way to solve your problem, seems to rewrite member/2 and delete/3, so a possibility could be:
singles([],[]).
singles([H | T], L) :-
member_(H, T),
delete_(T, H, Y),
singles(Y, L).
singles([H | T], [H | T1]) :-
\+member_(H, T),
singles(T, T1).
member_(_-H, [_-T|_]) :- H == T, !.
member_(E, [_|R]) :- member_(E, R).
delete_([], _, []).
delete_([_-T|Ts], F-H, Rs) :- T == H, !, delete_(Ts, F-H, Rs).
delete_([T|Ts], H, [T|Rs]) :- delete_(Ts, H, Rs).
that yields
?- singles([true-X, false-Y, false-X, true-Z],S).
S = [false-Y, true-Z]
You can see you underspecified your requirements: from your test case, seems we should delete every occurrence of false-VAR irrespectively of VAR...

Prolog: converting atom to new atom

I have a problem with predicate which works in that way that it takes list of atoms:
nopolfont([to,jest,tekśćik,'!'],L).
and in result
L = [to,jest,tekscik,'!'].
I have problem with make_swap and swap predicates. So far I have:
k(ś,s).
k(ą,a).
% etc.
swap(X,W) :- name(X,P), k(P,Y), !, name(Y,W).
swap(X,X).
make_swap(A,W)
:- atom(A),!,
name(A,L),
swap(L,NL),
name(W,NL).
nopolfont([],[]).
nopolfont([H|T],[NH|S]) :- make_swap(H,NH), nopolfont(T,S).
Is there any elegant way to do this?
This is also quite elegant:
polish_char_replacer(X, Y) :-
k(X, Y),
!.
polish_char_replacer(X, X).
nopolfont(Atoms1, Atoms2) :-
maplist(replace(polish_char_replacer), Atoms1, Atoms2).
replace(Goal, Atom1, Atom2) :-
atom_chars(Atom1, Chars1),
maplist(Goal, Chars1, Chars2),
atom_chars(Atom2, Chars2).
Probably as elegant as it can get:
k(ś,s).
k(ą,a).
swap(X,W) :- name(P,[X]), k(P,Y), !, name(Y,[W]).
swap(X,X).
list_swap([], []).
list_swap([H|T], [W|S]) :-
swap(H, W),
list_swap(T, S).
atom_swap(A,W) :-
atom(A), !,
name(A, L),
list_swap(L,S),
name(W, S).
nopolfont([],[]).
nopolfont([H|T],[NH|S]) :-
atom_swap(H,NH),
nopolfont(T,S).
Also, obviously define this, to get the expected result, but I assume this is in the % etc
k(ć, c).

Searching Prolog structures

I'm interested in formulae made up from lots of conjunctions (part of a larger problem). I want to write a program that takes something like this:
:- get_params(conj(conj(a,b),c),X)
and returns a list of all the parameters of the conjunctions i.e. X=[a,b,c]. At the moment I can do
:- get_params(conj(a,b),X) to get X=[a,b]
using simple Prolog pattern matching but how would you go about doing things such as
:- get_params(conj(conj(a,b),c),X) to get X=[a,b,c]
It seems really simple but I've been struggling all day!
Since you are describing a list, consider using DCG notation:
params(conj(A,B)) --> !, params(A), params(B).
params(X) --> [X].
Example:
?- phrase(params(conj(conj(a,b),c)), Ps).
Ps = [a, b, c].
Assuming that all conj functors are binary:
get_params(X, Y, L) :-
get_params(X, L1),
get_params(Y, L2),
append(L1, L2, L).
get_params(conj(X, Y), L) :-
get_params(X, Y, L), !.
get_params(A, [A]).

Prolog: Getting unique atoms from propositional formulas

I can easily write a predicate to get unique elements from a given list in Prolog e.g.
no_doubles( [], [] ).
no_doubles( [H|T], F ) :-
member( H, T ),
no_doubles( T, F ).
no_doubles( [H|T], [H|F] ) :-
\+ member( H, T ),
no_doubles( T, F ).
However, how can you do the same thing but for something other than a normal list i.e. not something like [a,b,c...]? So in my case, I want to extract unique atoms for a propositional formula e.g. unique_atoms(and(x,and(x,y),z),[x,y,z]). is satisfied. Do you use recursion just like in my no_doubles example but for a formula like this?
Any ideas are welcomed :). Thanks.
So you need to process a general term (i.e. a tree structure) and get a list of its atomic leaf nodes, without duplicates. Does the result list have to have a specific order (e.g. depth-first left-to-right), or is this not important?
If you have an option to use variables instead of atoms in your formulas then you can use the (SWI-Prolog) builtin term_variables/2, e.g.
?- term_variables(and(X, and(X, Y), Z), Vars).
Vars = [X, Y, Z].
Otherwise you have to go with a solution similar to:
term_atoms(Term, AtomSet) :-
term_to_atomlist(Term, AtomList),
list_to_set(AtomList, AtomSet).
term_to_atomlist(Atom, [Atom]) :-
atom(Atom),
!.
term_to_atomlist(Term, AtomList) :-
compound(Term),
Term =.. [_ | SubTerms],
terms_to_atomlist(SubTerms, AtomList).
terms_to_atomlist([], []).
terms_to_atomlist([Term | Terms], AtomList) :-
term_to_atomlist(Term, AtomList1),
terms_to_atomlist(Terms, AtomList2),
append(AtomList1, AtomList2, AtomList).
Usage example:
?- term_atoms(f(x^a1+a3*a3/a4)='P'-l, Atoms).
Atoms = [x, a1, a3, a4, 'P', l].
You might want to extend it to deal with numbers and variables in the leaf nodes.
?- setof(X, member(X,[a,b,c,a,b,c]), L).
L = [a, b, c].
?- sort([a,b,c,a,b,c], L).
L = [a, b, c].
Propositional formulas:
get_atoms(X,[X]) :-
atom(X).
get_atoms(and(P,Q),Atoms) :-
get_atoms(P,Left),
get_atoms(Q,Right),
append(Left,Right,Atoms).
etc. Optimize using difference lists if necessary.
unique_atoms(P,UniqueAtoms) :- get_atoms(P,Atoms), sort(Atoms,UniqueAtoms).
A more direct way is to use sets.

Resources