2

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.
heisenberg7584
  • 563
  • 2
  • 10
  • 30
  • There is something broken with your terms in the second and third example. – Willem Van Onsem Jan 17 '19 at 11:51
  • which one's boken? list_to_term - i am not sure if I did it correctly... The other one is just an exercise to be done – heisenberg7584 Jan 17 '19 at 11:56
  • the call `count(a,f(a,g(b,a),N).` has no "balancing" brackets. – Willem Van Onsem Jan 17 '19 at 11:56
  • it's copied from exercise... But probably it should be count(a,f(a,g(X,a)),N). – heisenberg7584 Jan 17 '19 at 11:57
  • Hint: recursively "unpack" the functor, and keep track of the number of times an argument matches. – Willem Van Onsem Jan 17 '19 at 11:59
  • In addition to Willem's comments, when you implement `count` (which, by the way, should be called something more specific, like, `count_argument`, for clarity) use `==/2` rather than `=/2` to check for an argument match. – lurker Jan 17 '19 at 12:25
  • @Willem so sth like: count(A,T,N) :- T =.. [F|L], count(A,F,N1), count(A,L,N2), N is N1+N2 ? – heisenberg7584 Jan 17 '19 at 13:01
  • @heisenberg7584: perhaps it is better to first construct a predicate that can enumerate all arguments (and sub arguments recursively). – Willem Van Onsem Jan 17 '19 at 13:04
  • @heisenberg7584 your suggestion for a recursive solution isn't quite right since you're mixing kinds of terms in one of the arguments.`count` accepts a compound term as its second argument, but `count(A, L, N2)` uses a *list of terms* in `L`, so this won't work (unless you write `count` to handle that case as well). – lurker Jan 17 '19 at 13:52

3 Answers3

1

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).

damianodamiano
  • 2,528
  • 2
  • 13
  • 21
  • thanks! @damianodamiano, looks like a pretty decent solution but really long and complicated, comparing to exercises I have I think that it must be a bit simpler to be done... But still thank you a lot – heisenberg7584 Jan 17 '19 at 13:40
1

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.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
0

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.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555