Use findall with arg - prolog

I'm learning Prolog and I try to rewrite the univ predicate:
?- foo(hello, X) =.. List.
List = [foo, hello, X]
?- Term =.. [baz, foo(1)].
Term = baz(foo(1))
I already wrote a first version that works well:
get_args(_, Arity, Arity, []).
get_args(T, Arity, N, [Arg|Args]) :-
I is N + 1,
arg(I, T, Arg),
get_args(T, Arity, I, Args).
univ(T, [Functor|Args]) :-
length(Args, Arity),
functor(T, Functor, Arity),
get_args(T, Arity, 0, Args),
!
I wanted to try another way to implement it. So, I rewrite this one by using findall and arg:
univ(T, [Functor|Args]) :-
length(Args, Arity),
functor(T, Functor, Arity),
findall(Arg, arg(_, T, Arg), Args),
!.
This one doesn't work well. Here is an example:
?- univ(a(C, D, E), L).
L = [a, _G1312, _G1315, _G1318].
?- univ(T, [a, C, D, E]).
T = a(_G1325, _G1326, _G1327).
Thus, I have a simple question: is it possible to use arg with findall in order to retrieve the name of each arguments?

As noted by #mat, findall/3 copies variables. You can use bagof/3 instead:
univ(T, [Functor|Args]) :-
length(Args, Arity),
functor(T, Functor, Arity),
bagof(Arg, I^arg(I, T, Arg), Args),
!.
?- univ(a(C, D, E), L).
L = [a, C, D, E].
This different behaviour wrt findall/3 and setof/3 can also be useful when you need to handle attributed variables.

Related

Convert a list from findall predicate to a string prolog

I am struggling to identify a way to convert a list to string. This list is the output from a findall predicate.
see below my code.
edge(a,b).
edge(a,c).
edge(b,c).
edge(c,d).
edge(c,e).
edge(d,e).
edge(f,g).
edge(g,h).
route(X, Z, []) :- edge(X, Z).
route(X, Z, [Y|T]) :- edge(X, Y), route(Y, Z, T).
allways(X, Y) :- findall(E, (route(X, Y, E), write(E), nl), _).
%allways(X, Y) :- findall(E, (route(X, Y, E), with_output_to(atom(_),maplist(write, E)), nl), _).
%allways(X, Y) :- findall(E, (route(X, Y, E), atomic_list_concat(E,'',A), nl), _),write(A).
my output
?- allways(a,e).
[b,c]
[b,c,d]
[c]
[c,d]
expected output
?- allways(a,e).
bc
bcd
c
cd
I tried different ways to convert the list to string using with_output_to predicate and atomic_list_concat predicate but nothing works.
When you say "string", what do you mean?
There are many predicates for concatenating a list to an atom. In your case atomic_list_concat/2 sounds about right.
Your problem however is in how you wrote the findall, you are capturing the wrong variable.
?- findall(Str, ( route(a, e, E), atomic_list_concat(E, Str) ), Strs),
forall(member(Str, Strs), format("~w~n", [Str])).
or, if you do not need the list at all, do not use findall at all.
?- forall(( route(a, e, E),
atomic_list_concat(E, Str)
),
format("~w~n", [Str])).

Creating a Prolog predicate via macro-like meta programming

I need a way for this to work:
?- create_pred(f, [A, B], (write(That), write(B), write(A), write(This))).
true.
?- f(this, a, b).
_L154bathis
true.
I have it working without the This so far:
?- create_pred(f, [A, B], (write(That), write(B), write(A))).
true.
?- f(a, b).
_L154ba
true.
The code for that is the following:
create_pred(Name, Args, Body) :-
length(Args, Argc),
functor(F, Name, Argc),
term_variables(F, Vars),
term_variables(Args, Vars),
assertz((F :- Body)).
Any idea how I could get that first argument unified with the This (and not the That) variable from the body?

Assert higher-order clause in Prolog

This works:
assert(p(X) :- q(X)).
This does not work:
P = p,Q = q, assert(P(X) :- Q(X)).
How can I make the latter work?
You need to make the terms first; you can use the "univ" operator, =.. for this:
?- P = p, Q = q, Head =.. [P, X], Body =.. [Q, X], assertz((Head :- Body)).
P = p,
Q = q,
Head = p(X),
Body = q(X).
?- listing(p/1).
:- dynamic p/1.
p(A) :-
q(A).
You need the second pair of parentheses in most implementations, apparently. You will need them anyway if you had for example a conjunction in the body.
?- assertz(a :- b).
true.
?- assertz(a :- b, c).
ERROR: assertz/2: Uninstantiated argument expected, found c (2-nd argument)
?- assertz((a :- b, c)).
true.

Prolog string manipulation

I have a string like 'pen,pencil,eraser'. How can I make this predicate.
things(pen,pencil,eraser).
Do you have any idea? ( I use prolog)
Here's a small example of specialized Prolog code for your problem which should work on most implementations (not only SWI-Prolog, but GNU Prolog, SICStus, etc.):
make_term(Functor, StringArgs, Term) :-
split_atom(StringArgs, ',', Args),
Term =.. [Functor|Args].
split_atom(A, E, L) :-
atom_chars(A, C),
split_atom2(C, E, L).
split_atom2([], _, []).
split_atom2(C, E, [A|L]) :-
append(C0, [E|C1], C), !,
atom_chars(A, C0),
split_atom2(C1, E, L).
split_atom2(C, _, [A]) :-
atom_chars(A, C).
Testing it out:
?- make_term(things, 'pen,pencil,eraser', T).
T = things(pen, pencil, eraser).
if you use swi-prolog, you can create this first: 'things(pen,pencil,eraser)' and then use term_to_atom/2
so something like:
get_term(Term):-
atom_concat('things(','pen,pencil,eraser',Temp),
atom_concat(Temp,')',A),
term_to_atom(Term, 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