Unexpected behavior of this prolog program - prolog

So for hw I need to make a program that finds a hole in a list of integers. The integers are always positive and there are no duplicates. Th following code works perfectly and always returns a list containing the hole. But its major flaw is that it only handles integer lists up to 9. I tried to fix this issue with by increment but with no luck. I always get a stack overflow. I cannot find out why. I thought all of my terminal predicates were correct. I have been using trace but the output just confuses me, it continues to increment.
game(Input, Hole) :-
sort(Input, Sortedinput),
compareheads(Sortedinput, [1, 2, 3, 4, 5, 6, 7, 8, 9], Hole).
compareheads([], IntegerList, []).
compareheads([], Anotherlist, Output).
compareheads(Compinput, [N|Restofintegers], [N|Output]) :-
compareheads(Compinput, Restofintegers, Output).
compareheads([H|Restofinput], [H|Restofintegers], Output) :-
compareheads(Restofinput, Restofintegers, Output).
This is the program that doesn't work...
game(Input, Hole) :-
sort(Input, Sortedinput),
compareheads(Sortedinput, 1, Hole).
compareheads([], IntegerList, []).
compareheads([], Anotherlist, Hole).
compareheads(List, 100, []).
compareheads(Compinput, Incr, [Incr|Hole]) :-
incr(Incr, Next),
compareheads(Compinput, Next, Hole).
compareheads([Incre|Restofinput], Incre, Hole) :-
incr(Incre, Another),
compareheads(Restofinput, Another, Hole).
incr(X, Y) :-
Y is X + 1.

Related

Prolog tries to find multiple solutions when only one exists

I've made a basic predicate ascending/1 to check if a list is in ascending order, on https://swish.swi-prolog.org.
ascending([]).
ascending([_]).
ascending([X, Y| T]) :-
X =< Y,
ascending([Y|T]).
It shows the following if I query ?- ascending([1, 2, 4, 6]).:
As in, it tries to find more solutions. Pressing Next, 10, 100 or 1,000 just returns false, which is a mystery in and of itself - true and false at the same time? Maybe that's because of the anonymous _? Have I not defined completely enough? Why is it not just returning true?
Most Prolog systems implement first-argument indexing, which allows avoid creating spurious choice-points. Assuming that and a call with the first argument bound, in the case of your code, the Prolog runtime is able to able to distinguish between the first clause, whose first argument is an atom, and the two other clauses, whose first argument are lists. But not able (in general) to distinguish between the second and third clauses and avoid trying both for a goal where the first argument is a list. This results in the creation of a choice-point. Hence the results your get:
?- ascending([1, 2, 4, 6]).
true ;
false.
But we can improve on your solution. For example:
ascending([]).
ascending([Head| Tail]) :-
ascending(Tail, Head).
ascending([], _).
ascending([Head| Tail], Previous) :-
Previous =< Head,
ascending(Tail, Head).
We will now get:
?- ascending([1, 2, 4, 6]).
true.
?- ascending([1, 2, 4, 6, 1]).
false.

Prolog arguments are not sufficiently instantiated (function which calculates list length)

I made a function which calculates a list length. Below is my code.
listLength(LIST) :- solve(LIST, LENGTH), write(LENGTH).
solve([], _).
solve([_|T], LENGTH) :- ADD is LENGTH + 1, solve(T, ADD).
When I run this code with input
?- listLength([1, 2, 3, 4, 5, 6, 7]).
then, interpreter showed me error message solve/2 : Arguments are not sufficiently instantiated.
when I modified above code like below.
listLength(LIST) :- LENGTH is 0, solve(LIST, LENGTH), write(LENGTH).
solve([], _).
solve([_|T], LENGTH) :- ADD is LENGTH + 1, solve(T, ADD).
When I run this code with the same input, then always 0 is written.
I want to calculate LENGTH, and I want to use the variable in listLength function.
What's wrong with me? ( Please kindly note that I am using swi-prolog. )
The first mistake is in the base case. Instead of solve([], _). you should write solve([], 0). because if you don't know the length of empty list how will you find recursively for bigger lists.
There is also another issue in :
solve([_|T], LENGTH) :- ADD is LENGTH + 1, solve(T, ADD).
when trying to calculate ADD is LENGTH + 1 length is not instantiated-calculated. You need to change the order like:
listLength(LIST) :- solve(LIST, LENGTH), write(LENGTH).
solve([], 0).
solve([_|T], LENGTH) :- solve(T, ADD), LENGTH is ADD+ 1.
Now querying:
?- listLength([1, 2, 3, 4, 5, 6, 7]).
7
true.

List from nested predicates

I want to know if it's possible to get a list of numbers from nested predicates in prolog.
I'll make an example, from:
?- elements(p(f(0,5,1), k(8, f(7,3), h(6)), 5), X).
I want in X this:
X = [0,5,1,8,7,3,6,5].
Thank you if you can help me =)
Whenever you write predicates involving general term traversal, always keep in mind that such predicates will be limited in the way they can be used. Let's call your relation term_subtermnumbers/2 which relates a term to the list of numbers that occur in it as subterms in the order of their left-to-right appearance, including multiple occurrences. First, you may think of examples you gave, like
?- term_subtermnumbers(p(f(0,5,1), k(8, f(7,3), h(6)), 5), Numbers).
Numbers = [0, 5, 1, 8, 7, 3, 6, 5].
But what if you turn the query around, and ask instead:
?- term_subtermnumbers(Term, [0, 5, 1, 8, 7, 3, 6, 5]).
There are many possibilities for solutions. In fact, infinitely many. Or take a simpler query:
?- term_subtermnumbers(Term, []).
That is, all the Terms that do not contain numbers.
Since the set of solutions is infinite here, and there is no way to abbreviate that set meaningfully as answers, it really makes sense to produce in that case a special error called an instantiation error.
Some - not all - Prolog built-ins ensure this property. (=..)/2 and functor/3 are faithfully guaranteeing that property. Helas, number/1 is not. As a consequence, always use (=..)/2 or functor/3 prior to number/1, atom/1, atomic/1 and some others.
term_subtermnumbers(Term, Numbers) :-
phrase(subtermnumbers(Term), Numbers).
subtermnumbers(Term) -->
{ Term =.. [_| Args] },
( {number(Term)} -> [Term]
; args_subtermnumbers(Args)
).
args_subtermnumbers([]) --> [].
args_subtermnumbers([Arg|Args]) -->
subtermnumbers(Arg),
args_subtermnumbers(Args).
If your Prolog has append/2 and maplist/3:
elements(N, [N]) :- number(N), !.
elements(S, Ss) :- S=..[_|Es], maplist(elements, Es, Ts), append(Ts, Ss).
This worked for me:
?- elements(p(f(0,5,1), k(8, f(7,3), h(6)), 5), X), writenl(X), fail.
elements(X,X) :- integer(X).
elements([],[]).
elements([X|Y],Z) :- integer(X), elements(Y,V), Z=[X|V].
elements([X|Y],Z) :- elements(X,W), elements(Y,V), append(W,V,Z).
elements(_(|Y),Z) :- elements(Y,Z).
writenl(X) :- write(X), nl.
It gave me [0, 5, 1, 8, 7, 3, 6, 5].
Try this instead:
?- elements(p(f(0,5,1), k(8, f(7,3), h(6)), 5), X), writenl(X), fail.
elements(X,X) :- integer(X).
elements([],[]).
elements([X|Y],Z) :- integer(X), elements(Y,V), Z=[X|V].
elements([X|Y],Z) :- elements(X,W), elements(Y,V), append(W,V,Z).
elements(X,Z) :- X=..[_|Y], elements(Y,Z).
writenl(X) :- write(X), nl.

Removing first occurrence in list - prolog

I need to remove only one occurrence in the list. Actually doesn't matter if it's first or last. One match needs to be removed.
I'm having trouble understanding why the following doesn't work as intended.
deleteOne(_,[],[]).
deleteOne(Term, [Term|Tail], Result) :-
deleteOne(Term, [], [Result|Tail]), !.
deleteOne(Term, [Head|Tail], [Head|TailResult]) :-
deleteOne(Term, Tail, TailResult), !.
Output
41 ?- deleteOne(5,[2,3,1,5,2,3,1],X).
X = [2, 3, 1, 5, 2, 3, 1].
It works when I replace term with an empty String or some random String.
deleteOne(Term, [Term|Tail], Result) :-
deleteOne("", Tail, Result), !.
Output
41 ?- deleteOne(5,[2,3,1,5,2,3,1],X).
X = [2, 3, 1, 2, 3, 1].
But I don't think this is the best solution for many reasons. Not for my current problem, but for example longer lists. Or if a list contains empty String - don't know if this is possible in Prolog.
Why wont the first example work? And what other solutions are there?
Your first one doesn't work because this doesn't make much sense:
deleteOne(Term, [Term|Tail], Result) :-
deleteOne(Term, [], [Result|Tail]), !.
That means the result of the next one has to have the current result as its head.
An better solution would be this:
delete_one(_, [], []).
delete_one(Term, [Term|Tail], Tail).
delete_one(Term, [Head|Tail], [Head|Result]) :-
delete_one(Term, Tail, Result).
If you want it to be determinative, add a cut on the second clause. As is, it can do this:
?- delete_one(2, [1, 2, 3, 1, 2, 3], X).
X = [1,3,1,2,3] ? ;
X = [1,2,3,1,3] ? ;
X = [1,2,3,1,2,3] ? ;
no
To delete only the first occurrence of an item X from a list L.Here I used cut operation.
delete(X,[X|T],T):-!.
delete(X,[Y|T],[Y|T1]):-delete(X,T,T1).

prolog lists and list manipulation

i am trying to write a binary predicate to take one list, compute mod 5 for each element and then put it in another list. so far, i have done this,
mod5(X,L):- R = [], modhelper(R,L), write(R).
modhelper(X,L):- memb(E,L), mod2(E,Z), addtolist(Z,X,X), modhelper(X,L).
%Get an element from the list L.
memb(E,[E|_]).
memb(E,[_|V]):- memb(E,V).
%If element is integer, return that integer mod 5 else return as is.
mod2(N,Z):- isInt(N) -> Z is N mod 5 ; Z = N.
%add this modified element to the output list.
addtolist(Y,[],[Y]).
addtolist(Y,[H|T],[H|N]):- addtolist(Y,T,N).
memb,mod2, addtolist work as expected but I'm doing something wrong in modhelper which I'm not able to figure out.
Any help is appreciated.
In SWI-Prolog:
mod5(X, Y) :-
Y is X mod 5.
apply_mod5_to_list(L1, L2) :-
maplist(mod5, L1, L2).
Usage:
?- apply_mod5_to_list([2, 4, 6, 8], L2).
L2 = [2, 4, 1, 3].
?- apply_mod5_to_list([2, 4.1, 6, 8], L2).
ERROR: mod/2: Type error: `integer' expected, found `4.1'
?- apply_mod5_to_list([2, not_number, 6, 8], L2).
ERROR: is/2: Arithmetic: `not_number/0' is not a function
You can easily modify this code if you want a slightly different behavior, e.g. if you want to tolerate non-integers (why do you want that btw?).
In case you cannot use maplist, you can implement it yourself, at least a more specialized version of it, e.g. something like this:
partition_the_list_into_first_and_rest([X | Xs], X, Xs).
% The result on an empty list is an empty list
apply_mod5_to_list([], []).
% If the input list contains at least one member
apply_mod5_to_list(L1, L2) :-
partition_the_list_into_first_and_rest(L1, X, Xs),
call(mod5, X, Y),
partition_the_list_into_first_and_rest(L2, Y, Ys),
apply_mod5_to_list(Xs, Ys).
To this code you can still apply a lot of syntactic simplification, which you should probably do to turn it into an acceptable homework solution...

Resources