Prolog - transforming to tail recursion - prolog

As a newbie in Prolog, I learned that tail recursion is optimized. Therefore, I am trying to convert the following program to tail-recursive ones.
sum([], 0).
sum([H|T], N):-
sum(T, X),
N is X + H.
Here is what I've tried, and it's obvious that I missed something in my logics:
sum(List,Sum):-
sum1(List,0,Sum).
sum1([Element|List],Accumulator,Sum):-
(NewAccumulator is Accumulator + Element,
sum1(List,NewAccumulator,Sum);
List=[] -> Sum = Accumulator
).
The problem in my program is adding all the numbers except the last one in the list. How can I improve this program? Thanks.

The problem is that you wrote the program wrongly. This would be correct:
sum(List, Sum) :-
sum_1(List, 0, Sum).
sum_1([], Sum, Sum).
sum_1([H|T], Sum0, Sum) :-
Sum1 is Sum0 + H,
sum_1(T, Sum1, Sum).
but you could have googled some version of this here on stackoverflow.
This is also trivially a fold on a list:
sum([H|T], Sum) :-
foldl(add, T, H, Sum).
add(X, Y, Z) :- Z is X + Y.

I agree with TA_intern's solution, but here is an addition to show you why your program did go wrong.
The following program leaves as much of your code as possible, but is correct:
sum(List,Sum):-
sum1(List,0,Sum).
sum1([Element|List],Accumulator,Sum):-
NewAccumulator is Accumulator + Element,
(sum1(List,NewAccumulator,Sum);
List=[] -> Sum = NewAccumulator
).
You had List=[] -> Sum = Accumulator, that means that in case the tail of your list was empty, you took Accumulator, which is the sum of all previous elements before Element.
An alternative that preserves even more of your code is:
sum1([Element|List], Accumulator, Sum) :-
( NewAccumulator is Accumulator+Element,
sum1(List, NewAccumulator, Sum)
; List=[]
-> Sum is Accumulator+Element
).
However, I personally would prefer TA_intern's solution.

Related

Predicate in prolog

I need to define a predicate in prolog which takes a list as input and sums the squares of numbers >= 5 and subtract sum of absolute value of numbers <=2.
This is what I have currently :-
pred([], 0).
pred([Head|Tail], Result) :-
gr85(Head),
pred(Tail, Total),
Result is Head*Head + Total.
pred([Head|Tail], Result) :-
leq2(Head),
pred(Tail, Total),
Result is Total - Head.
gr85(Number):-
Number >= 5.
leq2(Number):-
Number =< 2.
My question is how do I exclude anything between 2 and 5. If I input 3 in the list, it returns false.
Expected input
pred([3,6,2,-1], Result).
Expected output
Result= 33 (6*6 -2-1)
add a 'skip all' clause:
weird_sum([_|Tail], Result) :- weird_sum(Tail, Result).
I think you will gain some insight into Prolog working when you analyze where this clause should be added.
Maybe it is simpler if you keep it more simple, you can use accumulator to make it simpler. And also it is maybe less typing if you move the condiditional out, like this;
weird_sum(List, Sum) :-
weird_sum(List, 0, Sum).
weird_sum([], Acc, Sum) :- Sum is Acc.
weird_sum([X|Xs], Sum0, Sum) :-
once( weird_sum_helper(X, Sum0, Sum1) ),
weird_sum(Xs, Sum1, Sum).
weird_sum_helper(X, Acc, Acc + X*X) :- X >= 5.
weird_sum_helper(X, Acc, Acc - abs(X)) :- X =< 2.
weird_sum_helper(X, Acc, Acc) :- X < 5, X > 2.
But the actual answer to your original question is in the last line above, just say that when X is smaller than 5 AND X is larger than 2 than you don't do anything.
But I get different answer from 33 because when number is negative and you subtract you get positive not negative but I am not sure if this is what you mean.
I used once once just because you want to do this only once since the three conditions are excluding each other but somehow Prolog doesn't know that it should only take one and tries the others too.

Finding the max in a list - Prolog

I was just introduced to Prolog and am trying to write a predicate that finds the Max value of a list of integers. I need to write one that compares from the beginning and the other that compares from the end. So far, I have:
max2([],R).
max2([X|Xs], R):- X > R, max2(Xs, X).
max2([X|Xs], R):- X <= R, max2(Xs, R).
I realize that R hasn't been initiated yet, so it's unable to make the comparison. Do i need 3 arguments in order to complete this?
my_max([], R, R). %end
my_max([X|Xs], WK, R):- X > WK, my_max(Xs, X, R). %WK is Carry about
my_max([X|Xs], WK, R):- X =< WK, my_max(Xs, WK, R).
my_max([X|Xs], R):- my_max(Xs, X, R). %start
other way
%max of list
max_l([X],X) :- !, true.
%max_l([X],X). %unuse cut
%max_l([X],X):- false.
max_l([X|Xs], M):- max_l(Xs, M), M >= X.
max_l([X|Xs], X):- max_l(Xs, M), X > M.
Ignoring the homework constraints about starting from the beginning or the end, the proper way to implement a predicate that gets the numeric maximum is as follows:
list_max([P|T], O) :- list_max(T, P, O).
list_max([], P, P).
list_max([H|T], P, O) :-
( H > P
-> list_max(T, H, O)
; list_max(T, P, O)).
A very simple approach (which starts from the beginning) is the following:
maxlist([],0).
maxlist([Head|Tail],Max) :-
maxlist(Tail,TailMax),
Head > TailMax,
Max is Head.
maxlist([Head|Tail],Max) :-
maxlist(Tail,TailMax),
Head =< TailMax,
Max is TailMax.
As you said, you must have the variables instantiated if you want to evaluate an arithmetic expression. To solve this, first you have to make the recursive call, and then you compare.
Hope it helps!
As an alternative to BLUEPIXY' answer, SWI-Prolog has a builtin predicate, max_list/2, that does the search for you. You could also consider a slower method, IMO useful to gain familiarity with more builtins and nondeterminism (and then backtracking):
slow_max(L, Max) :-
select(Max, L, Rest), \+ (member(E, Rest), E > Max).
yields
2 ?- slow_max([1,2,3,4,5,6,10,7,8],X).
X = 10 ;
false.
3 ?- slow_max([1,2,10,3,4,5,6,10,7,8],X).
X = 10 ;
X = 10 ;
false.
edit
Note you don't strictly need three arguments, but just to have properly instantiated variables to carry out the comparison. Then you can 'reverse' the flow of values:
max2([R], R).
max2([X|Xs], R):- max2(Xs, T), (X > T -> R = X ; R = T).
again, this is slower than the three arguments loops, suggested in other answers, because it will defeat 'tail recursion optimization'. Also, it does just find one of the maxima:
2 ?- max2([1,2,3,10,5,10,6],X).
X = 10 ;
false.
Here's how to do it with lambda expressions and meta-predicate foldl/4, and, optionally, clpfd:
:- use_module([library(lambda),library(apply),library(clpfd)]).
numbers_max([Z|Zs],Max) :- foldl(\X^S^M^(M is max(X,S)),Zs,Z,Max).
fdvars_max( [Z|Zs],Max) :- foldl(\X^S^M^(M #= max(X,S)),Zs,Z,Max).
Let's run some queries!
?- numbers_max([1,4,2,3],M). % integers: all are distinct
M = 4. % succeeds deterministically
?- fdvars_max( [1,4,2,3],M).
M = 4. % succeeds deterministically
?- numbers_max([1,4,2,3,4],M). % integers: M occurs twice
M = 4. % succeeds deterministically
?- fdvars_max( [1,4,2,3,4],M).
M = 4. % succeeds deterministically
What if the list is empty?
?- numbers_max([],M).
false.
?- fdvars_max( [],M).
false.
At last, some queries showing differences between numbers_max/2 and fdvars_max/2:
?- numbers_max([1,2,3,10.0],M). % ints + float
M = 10.0.
?- fdvars_max( [1,2,3,10.0],M). % ints + float
ERROR: Domain error: `clpfd_expression' expected, found `10.0'
?- numbers_max([A,B,C],M). % more general use
ERROR: is/2: Arguments are not sufficiently instantiated
?- fdvars_max( [A,B,C],M).
M#>=_X, M#>=C, M#=max(C,_X), _X#>=A, _X#>=B, _X#=max(B,A). % residual goals
list_max([L|Ls], Max) :- foldl(num_num_max, Ls, L, Max).
num_num_max(X, Y, Max) :- Max is max(X, Y).
%Query will be
?-list_max([4,12,5,3,8,90,10,11],Max).
Max=90
Right now I was working with recursion in Prolog, so if it is useful for someone I will leave 'my two cents' solving it in the two ways that I have thought:
% Start
start :- max_trad([2, 4, 6, 0, 5], MaxNumber1),
max_tail([2, 4, 6, 0, 5], 0, MaxNumber2),
show_results(MaxNumber1, MaxNumber2).
% Traditional Recursion (Method 1)
max_trad([Head|Tail], Max) :- max_trad(Tail, Value), Head > Value, Max is Head.
max_trad([Head|Tail], Max) :- max_trad(Tail, Value), Head =< Value, Max is Value.
max_trad([], 0).
% Tail Recursion (Method 2)
max_tail([], PartialMax, PartialMax).
max_tail([Head|Tail], PartialMax, FinalMax) :- Head > PartialMax, max_tail(Tail, Head, FinalMax).
max_tail([_|Tail], PartialMax, FinalMax) :- max_tail(Tail, PartialMax, FinalMax).
% Show both of the results
show_results(MaxNumber1, MaxNumber2) :-
write("The max value (obtained with traditional recursion) is: "), writeln(MaxNumber1),
write("The max value (obtained with tail recursion) is: "), writeln(MaxNumber2).
The output of the above code is:
Both methods are similar, the difference is that in the second an auxiliary variable is used in the recursion to pass values forward, while in the first method, although we have one less variable, we are filling the Stack with instructions to be executed later, so if it were an exaggeratedly large list, the second method is appropriate.
maximum_no([],Max):-
write("Maximum No From the List is:: ",Max).
maximum_no([H|T],Max):-
H>Max,
N = H,
maximum_no(T,N).
maximum_no(L,Max):-
maximum_no(L,Max).
The maximum number in a list in Prolog ?
max([],A):-print(A),!.
max([Head | Tail] , A):-A =< Head ,A1 is Head , max(Tail,A1) ; max(Tail,A).
max(L,M):-
member(M,L),
findall(X,(member(X,L),X>M),NL),
length(NL,0).

Prolog: Create sublist, given two indices

Basically, I need to create a predicate of the form sublist(S,M,N,L), where S is a new list formed from the elements of L between index M and index N, inclusive.
Here's where I've gotten:
sublist([],_,_,[]).
sublist([],M,N,_) :- (M > N).
sublist(S,M,N,L) :- sublist2(S,M,N,L,-1).
sublist2([H|T],St,En,[H2|T2],Idx) :-
(Idx2 is Idx + 1,
St =< Idx2,
En >= Idx2,
H = H2,
sublist2(T,St,En,T2,Idx2);
Idx2 is Idx + 1,
sublist2(T,St,En,T2,Idx2)).
As with all my prolog problems, I feel I'm making it way more complicated than it should be. I've got the base cases right, but anything else evaluates to false. Any advice for this problem, and just general approach to prolog? I understand the language for the most part, but I can't seem to see the simple solutions.
Simple solutions follow simple outlook. For lists it's recursion. Recursive programming is simple - just imagine you already have your function, following the given interface/requirements, and so you get to use it whenever you feel like it (but better, in the reduced cases).
sublist(S,M,N,[_A|B]):- M>0, M<N, sublist(S,M-1,N-1,B).
think of it as stating a law of sublists: sublist in a shorter list starts at decreased index.
sublist(S,M,N,[A|B]):- 0 is M, M<N, N2 is N-1, S=[A|D], sublist(D,0,N2,B).
and,
sublist([],0,0,_).
it is exclusive in the second index. tweak it. :)
There is the possibility to handle indexing in a way similar to more traditional languages:
sublist(L, M, N, S) :-
findall(E, (nth1(I, L, E), I >= M, I =< N), S).
or equivalently
sublist(L, M, N, S) :-
findall(E, (between(M, N, I), nth1(I, L, E)), S).
nth1/3 is for indexing from 1, otherwise nth0/3 allows C style - start from 0. I've placed the sublist as last argument. It's a common convention in Prolog to place output parameters after input.
Here a (cumbersome) recursive definition
sublist(L,M,N,S) :- sublist2(1,L,M,N,S).
sublist2(_,[],_,_,[]).
sublist2(I,[X|Xs],M,N,[X|Ys]) :-
between(M,N,I),
J is I + 1,
!, sublist2(J,Xs,M,N,Ys).
sublist2(I,[_|Xs],M,N,Ys) :-
J is I + 1,
sublist2(J,Xs,M,N,Ys).

Prolog - How to calculate the total number of elements

How do you calculate the total number of elements in a list when there can be lists within a list? (add the length of all the lists together)
?- sumOfLists([[1,2],[],[a,s,d],[a,1,3],[1]],S).
S = 9
Here is the shorter way I know, using flatten/2:
sumOfLists(Ls, S) :-
flatten(Ls, Flat),
length(Flat, S).
You can do this without flattening and without using maplist.
Here is my proposal:
%element atomic? -> count
deep_len([H|T],N) :- atomic(H), deep_len(T, M), N is M + 1.
%element list? -> recurse for head and tail and sum results
deep_len([H|T],N) :- is_list(H), deep_len(H, Sum1), deep_len(T, Sum2), N is Sum1+Sum2.
First, use maplist to replace each list by it's length, then sum up the lengths.
Using a sum predicate like this one:
sum(L, S) :- sum(L, S, 0).
sum([], Acc, Acc).
sum([X|Xs], Sum, Acc) :-
Acc1 is Acc + X,
sum(Xs, Sum, Acc1).
it boils down to just:
sumOfLists(L, S) :- maplist(length, L, Lengths), sum(Lengths, S).

Prolog Tail Recursive procedure to count uninstantiated variables in list

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

Resources