Prolog Tail Recursive procedure to count uninstantiated variables in list - prolog

I am trying to write a Tail Recursive procedure to count the number of uninstantiated variables in a list. I am a little stuck, where am I going wrong.
My current query is below:
count([S,L],N) :- var(S), !, N+1.
count([L],N).

Note: this answer presents a solution that is recursive but not tail recursive. For a tail recursive solution you should use an accumulator as can be shown in other answers from this question.
As with any recursive procedure, you should add a proper base case.
In this case, it should be a clause with an empty list that returns unifies 0 with the number of uninstantiated variables:
count([], 0).
Check the clause you've written. It takes as input a list of two elements instead of a list represented as a Head item and a tail list, and it really does nothing with N:
count([Head|Tail], M):-
var(Head),
!,
count(Tail, N),
M is N+1.
And finally, you should also add a clause to deal with the case when the first item of the list is not an uninstantiated variable:
count([_|Tail], N):- count(Tail, N).

Here is a tail recursion for counting variables in a list. It uses the technique of accumulators:
count(L, N) :- count(L, 0, N). % L=list, N=count, 0=value of the sum accumulator S
count([], S, S) :- !. % the innermost call, the accumulator S (2nd arg) "copied" to final result (3rd arg)
count([H| T], S, N):- var(H), !, S1 is S+1, count(T, S1, N). % increase accumulator if H is var
count([H| T], S, N):- count(T, S, N). % keep accumulator if H is not var
No calls follow the last recursive calls in all clauses.

There is no recursion here, because in order to have recursion, you must define something in terms of itself - you'll notice an absence of the count/2 rule on the right hand side in your code.
% two paths, variable and non-variable
% and a base case to start the count
count([S|L], N) :- var(S), !, count(L, N0), N is N0+1.
count([S|L], N) :- nonvar(S), !, count(L, N).
count([], 0).
Alternatively, this can be done simply with findall/3.
count_alt(L, N) :- findall(S, (member(S, L), var(S)), D), length(D, N).

Related

Predicate returning false

I'm having trouble with the following predicate:
treeToList(void, []).
treeToList(arbol(X, HI1, HD1), L) :-
treeToList(HI1, L1),
treeToList(HD1, L2),
append(L1, [X|L2], L).
maximumInList([X], X).
maximumInList([A|L], X) :-
maximumInList(L,X1),
(A > X1 -> X = A; X = X1).
maxNodeInTree(arbol, N) :-
treeToList(arbol, L),
maximumInList(L, N).
TreeToList gets a tree and returns a list with all of its nodes. Meanwhile maximumInList gets a list and returns the maximum element in the list.
Both of these predicates work fine individually, however the last one, maxNodeInTree, is supposed to first get the list L using treeToList which then will be passed to maximumInList and it'll return the maximum element in the whole tree. And yet Prolog returns false.
Any tips are appreciated!
You have a typo in the last predicate (arbol instead of Arbol). Try:
maxNodeInTree(Arbol, N) :-
treeToList(Arbol, L),
maximumInList(L, N).

How to combine list operations?

I would like to count how often element x is in list L.
I know there is a way with recursion, but is it also possible like this:
> count(X,L,C) = C is (length(L,Length) - length(delete(L, X,
> List2),Length)).
?
The compiler say no:) What could I change?
No, since length(L, Length) is not a function. It is a predicate, so it is True or False (or gets stuck in an infinite loop, or errors, but you can argue that these are not really "results" of a predicate).
Furthermore, you can not call a predicate like length(delete(L, X, List2), Length), since, for the Prolog interpreter, the delete/3 is here just a functor. It will thus not call the delete/3 predicate.
You can however rewrite this to:
count(X, L, C) :-
length(L, N1),
delete(L, X, L2),
length(L2, N2),
C is N1 - N2.

SWI ProLog / ProbLog: Check if exactly one element in a list is true

I'm trying to make a function in ProbLog (extension of ProLog) that checks if only one of the inputs is True. As input the function will take a list of variables as argument. An XOR is not good enough in this case, because I am working with more than two arguments and I want the function to be true if and only if one of the elements in the list is True.
So for example:
function([X|Xs) :- code that checks if only (exactly) one element in the list is true
I don't know about the Problog extension, but maybe you can simply count the true elements and check if the count is one? Perhaps using this as a starting point:
length(List, N) :-
length(List, 0, N).
length([], N, N). % Second argument is the accumulator.
length([H|T], L, N) :-
L1 is L + 1,
length(T, L1, N).
You would then add a case where L is not incremented if H is not true, and in the end check if N is one.
hasOneTrueElement(List) :-
countTrueElements(List, 1).
countTrueElements(List, N) :-
countTrueElements(List, 0, N).
countTrueElements([], N, N).
countTrueElements([H|T], L, N) :-
call(H),
L1 is L + 1,
countTrueElements(T, L1, N).
countTrueElements([H|T], L, N) :-
\+ H,
countTrueElements(T, L, N).
this should work
list_unique(List, Unique) :-
select(Unique, List, Rest), \+ memberchk(Unique, Rest).

Fact for each element of a list Prolog

I want to solve this problem in Prolog. i want give a list of natural numbers to find all the elements in the list that satisfy this condition:
All elements on the left of it are smaller than it and all the elements on the right of it are larger than it.
For example give a list [3,2,4,1,5,7,8,9,10,8] the answer would be 5,7
So far I've manage to make this function that given an element of the list it returns true or false if the element satisfises the condition described above.
check(Elem, List) :-
seperate(Elem, List, List1, List2),
lesser(Elem, List1, X1),
bigger(Elem, List2, X2),
size(X1, L1),
size(X2, L2),
size(List, L3),
match(L1, L2, L3),
Now I want to make another predicate that given a list, it does the above computations for each element of the list. Due to the fact that more than one element may satisfy it I want to create another list with all the elements that satisfy the problem.
The question would be something like ?-predicate_name([[3,2,4,1,5,7,8,9,10,8],N). and the result would be a list of elements.
Sry If I am not using the right terms of Prolog. I will describe what I want to do in sequential logic language to be more specific although it's not a good idea to think like that. If we consider a the predicate check as a function that given a list and element of the list it would return true or false whether or not the element satisfied the conditions of the problem. Now I want to parse each element of the list and for each one of it call the function check. If that would return true then I would add the element in another list a.k.a result. I want to do this in Prolog but I don't know how to iterate a list.
Here is a version using DCGs and assuming we want to compare arithmetically.
list_mid(L, M) :-
phrase(mid(M), L).
mid(M) -->
seq(Sm),
[M],
{maplist(>(M),Sm)},
seq(Gr),
{maplist(<(M),Gr)}.
seq([]) -->
[].
seq([E|Es]) -->
[E],
seq(Es).
Often it is not worth optimizing this any further. The first seq(Sm) together with the subsequent maplist/2 might be merged together. This is a bit tricky, since one has to handle separately the cases where Sm = [] and Sm = [_|_].
mid(M) -->
( [M]
| max(Mx),
[M],
{Mx < M}
),
min(M).
max(M) -->
[E],
maxi(E, M).
maxi(E, E) -->
[].
maxi(E, M) -->
[F],
{G is max(F,E)},
maxi(G, M).
min(_) -->
[].
min(M) -->
[E],
{M < E},
min(M).
I'm going to take a different approach on the problem.
We want to find all of the values that meet the criteria of being a "mid" value, which is one defined as being greater than all those before it in the list, and less than all those after.
Define a predicate mid(L, M) as meaning M is a "mid" value of L:
mid([X|T], X) :- % The first element of a list is a "mid" if...
less(X, T). % it is less than the rest of the list
mid([X|T], M) :- % M is a "mid" of [X|T] if...
mid(T, X, M). % M is a "mid" > X
% (NOTE: first element is not a "mid" by definition)
mid([X|T], LastM, X) :- % X is a "mid" > Y if...
X > LastM, % X > the last "mid"
less(X, T). % X < the rest of the list, T
mid([X|T], LastM, M) :- % Also, M is a "mid" if...
Z is max(X, LastM), % Z is the larger of X and the last "mid"
mid(T, Z, M). % M is the "mid" of T which is > Z
less(X, [Y|T]) :- % X is less than the list [Y|T] if...
X < Y, % X < Y, and
less(X, T). % X < the tail, T
less(_, []). % An element is always less than the empty list
Each query will find the next "mid":
| ?- mid([3,2,4,1,5,7,8,9,10,8], M).
M = 5 ? ;
M = 7 ? ;
no
Then they can be captured with a findall:
mids(L, Ms) :-
findall(M, mid(L, M), Ns).
| ?- mids([3,2,4,1,5,7,8,9,10,8], Ms).
Ms = [5,7]
yes
| ?- mids([2], L).
L = [2]
(1 ms) yes
This is probably not the most computationally efficient solution since it doesn't take advantage of a couple of properties of "mids". For example, "mids" will all be clustered together contiguously, so once a "mid" is found, it doesn't make sense to continue searching if an element is subsequently encountered which is not itself a "mid". If efficiency is a goal, these sorts of ideas can be worked into the logical process.
ADDENDUM
With credit to #false for reminding me about maplist, the above predicate call less(X, T) could be replaced by maplist(<(X), T) eliminating the definition of less in the above implementation.

Traverse a list in Prolog

I have a problem like this: find all elements in a list such that all element(s) immediately besides it is/are odd numbers.
For example
?- find([20,1,2,3,4,5,6,7,8,10], L).
L = [20, 2, 4, 6]
Normally in other languages I would traverse the list and check the condition, but I don't know how to "think" in Prolog in this scenario. How should I approach this?
visit the list considering the pair of head elements:
find([A,B|R], [A|T]) :-
is_odd(B),
... etc etc
You'll need to add obviously the base recursion case and the case when A must be discarded.
EDIT: a better solution based on CapelliCs suggestion (this uses the isodd predicate from below):
% if N0 and N2 are odd, cut, add N1 to the result and recurse
ff([N0,N1,N2|T], [N1|R]) :- isodd(N0), isodd(N2), !, ff([N1,N2|T], R).
% for any other case where the list has at least three members, cut and recurse
ff([_,N1,N2|T], R) :- !, ff([N1,N2|T], R).
% this is reached if the list has less that three members - we're done
ff(_, []).
% append and prepend '1' to the list to deal with the edges, call ff.
find(L, R) :- append(L, [1], L1), ff([1|L], R).
My old solution which keept track of the two previous values with extra arguments:
% isodd(+N)
% helper predicate that succeds for odd numbers.
isodd(N) :- mod(N, 2) =:= 1.
% find(+I, +N1, +N2, +R, -L)
% find/5 is the predicate doing the actual work.
% I is the input list, N1 and N2 are the numbers before the current one,
% R is the intermediate result list and L the result.
% we're done if the input list is empty
find([], _, _, R, R) :- !.
% check if N0 and N2 are odd to see if N1 should be appended to the list.
% if yes, do a cut, append N1 to the result and recurse.
find([N0|T], N1, N2, R, L) :-
isodd(N0), isodd(N2), !,
append(R, [N1], R1), find(T, N0, N1, R1, L).
% if N0 and N2 are not odd (and thus the cut in the previous clause isn't
% reached) just continue the recursion.
find([N0|T], N1, _, R, L) :- find(T, N0, N1, R, L).
% find(+I, -L)
% this predicate is the entry point - initialize the result list and the first
% values for N1 and N2, and append 1 to the input list so we don't need an extra
% predicate for dealing with the last item.
find(I, L) :- append(I, [1], I1), find(I1, 1, 0, [], L).

Resources