Related
How do you write a Prolog procedure map(List, PredName, Result) that applies the predicate PredName(Arg, Res) to the elements of List, and returns the result in the list Result?
For example:
test(N,R) :- R is N*N.
?- map([3,5,-2], test, L).
L = [9,25,4] ;
no
This is usually called maplist/3 and is part of the Prolog prologue. Note the different argument order!
:- meta_predicate(maplist(2, ?, ?)).
maplist(_C_2, [], []).
maplist( C_2, [X|Xs], [Y|Ys]) :-
call(C_2, X, Y),
maplist( C_2, Xs, Ys).
The different argument order permits you to easily nest several maplist-goals.
?- maplist(maplist(test),[[1,2],[3,4]],Rss).
Rss = [[1,4],[9,16]].
maplist comes in different arities and corresponds to the following constructs in functional languages, but requires that all lists are of same length. Note that Prolog does not have the asymmetry between zip/zipWith and unzip. A goal maplist(C_3, Xs, Ys, Zs) subsumes both and even offers more general uses.
maplist/2 corresponds to all
maplist/3 corresponds to map
maplist/4 corresponds to zipWith but also unzip
maplist/5 corresponds to zipWith3 and unzip3
...
Last time I learnt about =.. that can translate a list to term and opposite.
I have 3 predicates to do, first one is the one that translates a list to a term. I came up with sth like this:
list_to_term(List, Functor, Term) :-
Term =.. [Functor | List].
Is it okey? Enough? Or I miss something?
The other predicate is count(A,T,N) for element A, in term T with number N that is true if N is a count of elements A in term T... Can anyone help me with this one or how to start?
?- count(a,f(a),N).
N = 1
?- count(a,f(a,g(b,a),N).
N = 2.
?- count(a,f(a,g(X,a),N).
N = 2.
Looking at the answer of this post you can reuse the predicate flatten_term/2, a little bit modified to handle free variables, to sove your problem. Here is the code for a basic solution:
flatten_term(Term,[Term]):-
(atomic(Term);var(Term)),!.
flatten_term(Term,Flat):-
Term =.. TermList,
flatten_term_list(TermList,Flat),!.
flatten_term_list([],[]):-!.
flatten_term_list([H|T],List):-
flatten_term(H,HList),
flatten_term_list(T,TList),
append(HList,TList,List),!.
occurrences(_,[],N,N):-!.
occurrences(A,[H|T],N,Tot):-
A \== H,!,
occurrences(A,T,N,Tot).
occurrences(A,[H|T],N,Tot):-
A == H,!,
N1 is N+1,
occurrences(A,T,N1,Tot).
count(A,Term,N):-
flatten_term(Term,Flatten),
occurrences(A,Flatten,0,N).
?- count(a,f(a,g(X,a),d),T).
T = 2.
?- count(X,f(a,g(X,a),d),T).
T = 1
First of all you flatten the term using flatten_term/2. Then simply count the occurrences of the element you want to find using occurrences/4. You can, if you want, modify flatten_term/2 to avoid the usage of occurrences/4 and so scan the term (list) only one time... Something like: flatten_term(Term,Flatten,ElementToFind,Counter,Total).
Start by solving a more general problem of counting the terms in a list. Processing a term is processing a singleton list containing that term, after all:
count(A,T,N):- count(A, [T|Z],Z, 0,N).
count(_, [], [], C,N):- N is C, !.
count(A, [T|B],Z, C,N):- ?=(A,T), A=T, !, count(A, B,Z, C+1,N).
count(A, [T|B],Z, C,N):- ?=(A,T), T=..[_|S], !, append(S,Y,Z), count(A, B,Y, C,N).
count(A, [_|B],Z, C,N):- count(A, B,Z, C,N).
This opens up each head term in a list in succession and appends its argument terms to that list thus using it as a queue... thus processing the predicate's second argument T in a breadth-first manner.
This assumes A argument is an atom, and ?= is used to avoid instantiating the free variables we might encounter, and instead to skip over them, as your examples seem to indicate.
Is it okey? Enough? Or I miss something?
Prolog's =../2 predicate [swi-doc] can "pack" and "unpack" a list that contains the functor name and its arguments in a term and vice versa. So one can use this to construct a term, or to analyze a term. For example:
?- f(a,g(b,a)) =.. L.
L = [f, a, g(b, a)].
Here f is the functor name, and a and g(b, a) are the arguments. These arguments can be terms as well, and then we thus need to unpack these arguments further.
We can for example obtain all the subterms of a term with:
subterms(T, T) :-
\+ var(T).
subterms(T, ST) :-
\+ var(T),
T =.. [_|As],
member(A, As),
subterms(A, ST).
For example:
?- subterms(f(a,g(X,a)),N).
N = f(a, g(X, a)) ;
N = a ;
N = g(X, a) ;
N = a ;
false.
Now that we obtained all (sub)terms, we can slightly rewrite the predicate to count the number of elements that match:
subterm_query(Q, T) :-
Q == T.
subterm_query(Q, T) :-
\+ var(T),
T =.. [_|As],
member(A, As),
subterm_query(Q, A).
so we obtain if we query for a:
?- subterm_query(a, f(a,g(X,a))).
true ;
true ;
false.
If we can use the aggregate library, we can make use of the aggregate_all/3 predicate to count the number of times, the predicate was succesful:
?- aggregate_all(count, subterm_query(a, f(a,g(X,a))), Count).
Count = 2.
If not, you need to implement a mechanism that returns 1 for a match, and sums up recursively the matches of the child terms. I leave this as an exercise.
I am using a higher order Prolog variant that lacks findall.
There is another question on implementing our own findall here: Getting list of solutions in Prolog.
The inefficient implementation is:
parent(pam, bob). %pam is a parent of bob
parent(george, bob). %george is a parent of bob
list_parents(A, Es, [X|Xs]) :-
parent(X, A),
\+ member(X, Es),
list_parents(A, [X|Es], Xs).
list_parents(A, Es, []).
The efficient one
need a "solutions" higher-order predicate:
list_parents(X, Ys) :- solutions(parent, [X, W], 1, Ys)
What is solutions? Can I implement my own solutions predicate in higher order lambda Prolog?
Yes, if findall/3 were not available, you could implement it for example via the dynamic database.
For example, for the concrete use case of parents:
list_parents(_) :-
parent(P, _),
assertz(parent(P)),
false.
list_parents(Ps) :-
phrase(retract_parents, Ps).
retract_parents -->
( { retract(parent(P)) } ->
[P],
retract_parents
; []
).
Sample query:
?- list_parents(Ps).
Ps = [pam, george].
You can combine this with sort/2 for asymptotically optimal performance, avoiding the quadratic overhead of the "naive" solution to remove duplicates.
Beware though: First, this is not thread-safe. To make it thread-safe you need to add more information pertaining to the current thread.
Second, if you implement full-fledged findall/3 in this way, you must take care of nested findall/3 calls.
One way to do this is to assert two kinds of terms:
solution(S), such as solution(parent(pam)), indicating a concrete solution that was found on backtracking via the most recent findall/3 call
mark, indicating that a new findall/3 starts here
When collecting solutions, you only proceed to the most recent mark.
See Richard O'Keefe's book for a good introduction to these issues.
If your Prolog has some kind of non backtrackable assignment, like SWI-Prolog 'global' variables, you could implement (beware, simple minded code) in this way:
:- meta_predicate solutions(0, ?).
:- meta_predicate solutions(+, 0, ?).
solutions(G, L) :-
solutions(G, G, L).
solutions(P, G, L) :-
( nb_current(solutions_depth, C) -> true ; C=1 ),
D is C+1,
nb_setval(solutions_depth, D),
atom_concat(solutions_depth_, D, Store),
nb_setval(Store, []),
( G,
nb_getval(Store, T),
nb_setval(Store, [P|T]),
fail
; nb_getval(Store, R)
),
nb_delete(Store),
nb_setval(solutions_depth, C),
reverse(R, L).
Usage of 'global' variables results in more efficient execution WRT the dynamic database (assert/retract), and (in SWI-prolog) can be used even in multithreaded applications.
edit
Thanks to #false comment, moved the cut(s) before reverse/2, and introduced a stack for reentrant calls: for instance
?- solutions(X-Ys,(between(1,3,X),solutions(Y,between(1,5,Y),Ys)),S).
S = [1-[1, 2, 3, 4, 5], 2-[1, 2, 3, 4, 5], 3-[1, 2, 3, 4, 5]].
edit
Here is a variant of solutions/3, building the result list in order, to avoid the final reverse/2 call. Adding results to the list tail is a bit tricky...
solutions(P, G, L) :-
( nb_current(solutions_depth, C) -> true ; C=1 ),
D is C+1,
nb_setval(solutions_depth, D),
atom_concat(solutions_depth_, D, Store),
( G,
( nb_current(Store, U/B) -> B = [P|R], Q = U/R ; Q = [P|T]/T ),
nb_setval(Store, Q),
fail
; ( nb_current(Store, L/[]) -> true ; L = [] )
),
nb_delete(Store),
nb_setval(solutions_depth, C).
I am writing a module which uses term_expansion/2 to process a prolog source file. While doing so, I deconstruct predicates using =.. or project to its name using functor/3.
For example:
?- functor(pred(foo, bar), N, _).
N = pred.
?- pred(foo, bar) =.. L.
L = [pred, foo, bar].
Now I want to change the name of pred to (for example) pred_expanded and make it a term again, so that pred(foo, bar) becomes pred_expanded(foo, bar).
I tried append(pred, "_expanded", F) and append(pred, '_expanded', F) without success.
change_functor(Term,NewFunctor,NewTerm) :-
Term =.. [_|Args],
NewTerm =.. [NewFunctor|Args].
term_expansion(Term,ExpandedTerm) :-
functor(Term,pred,2),
change_functor(Term,pred_expanded,ExpandedTerm).
For example:
?- term_expansion(pred(foo,bar),T).
T = pred_expanded(foo, bar).
Illustrates how to use the "univ" predefined predicate. However, this is the best solution:
term_expansion(pred(A,B),pred_expanded(A,B)).
If you need to translate any functor, use atom_concat/3 as stated before:
term_expansion(Term,ExpandedTerm) :-
functor(Term,F,_),
atom_concat(F,'_expanded',NewF),
change_functor(Term,NewF,ExpandedTerm).
Example:
?- term_expansion(kk(1,2,3),N).
N = kk_expanded(1, 2, 3).
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.