Useless "no" errors when writing Prolog sorting queries - sorting

I tried to write a Prolog query that would sort a given list. Tried two different attempts so far, both attempting to implement insertion sort, but I am unsure why each one failed. Currently using GNU Prolog version 1.5.0 (64 Bits).
Attempt 1: (tried to the insertion and the sorting both in the same query. Very imperative)
% Use: sortz([1,3,2], What). or sortz([1,3,2], [], What).
% Attempt to use insertion sort, not sure what's wrong.
% Result will be the last parameter when first element is empty, thus all elements will have been sorted into the second list
sortz([], Sorted, Sorted).
% If there is only 1 value, it is sorted
sortz([Head|Tail], [], Result) :- sortz(Tail, Head, Result).
% Upon encountering the first element smaller or equal to Head, insert Head at that location
sortz([Head|Tail], [SortedHead|SortedTail], Result) :- Head =< SortedHead, sortz(Tail, [Head|[SortedHead|SortedTail]], Result).
% If Head is bigger than current SortedHead, get the result of sorting Head with SortedTail, then continue sorting
sortz([Head|Tail], [SortedHead|SortedTail], Result) :- Head > SortedHead, sortz([Head], SortedTail, SortedRest), sortz(Tail, [SortedHead|SortedRest], Result).
% Shortcut
sortz(List, Result) :- sortz(List, [], Result).
Running sortz([1,3,2], What). and sortz([1,3,2], [], What). both return "no".
Attempt 2:
% Shortcut
insertionsort(List, Result) :- insertionsort(List, [], Result).
% Base case. Result will be the last parameter when first element is empty, thus all elements will have been sorted into the second list
insertionsort([], Result, Result).
insertionsort([Head|Tail], Acc, Result) :- insert(Head, Acc, Inter), insertionsort(Tail, Inter, Result).
% Base case
insert([], Result, Result).
% If X is smaller or equal to Head, insert X before Head
insert(X, [Head|Tail], Inter) :- X =< Head, insert([], [X|[Head|Tail]], Inter).
% If X is greater, insert X into the Tail, then append the sorted list onto head
insert(X, [Head|Tail], Inter) :- X > Head, insert(X, Tail, Sortedrest), insert([], [Head|Sortedrest], Inter).
% Base case
insert(X, [], [X]).
Running insertionsort([1,3,2], What). and insertionsort([1,3,2], [], What). both return "What = [1,2,3] ?" first, but upon entering ";", I get "uncaught exception: error(type_error(evaluable,[]/0),(=<)/2)".
Would appreciate any insight as to why these two code samples are wrong. I have already found working examples of sorts elsewhere, but I'm just stuck on why Attempt 1 in particular isn't working. Probably missing a case or two somewhere but I'm not sure how to debug Prolog's "no"s...

Looking at attempt 1 (using swi-prolog):
?- trace, sortz([2,1,3], S).
Call: (11) sortz([2, 1, 3], _13888) ? creep
Unify: (11) sortz([2, 1, 3], _13888)
Call: (12) sortz([2, 1, 3], [], _13888) ? creep
Unify: (12) sortz([2, 1, 3], [], _13888)
Call: (13) sortz([1, 3], 2, _13888) ? creep
Fail: (13) sortz([1, 3], 2, _13888) ? creep
That is wrong, because the 2nd argument should be a list, i.e. [2] instead of 2.
Replace the one line to be:
sortz([Head|Tail], [], Result) :- sortz(Tail, [Head], Result).
... and then it works.

Related

Finding number of unique elements in the list

I want to write a code which will return number of unique elements in the list. My idea is to check if head of the list is there in the list of uniqeue elemeents. If it is not, add it to the list and increment the counter and continue witht the remaining list. So, I tried out following:
count([],0).
count([X,T], N) :-
count([X,T], [], N). %initially list of unique elements is empty
count([X,T], U, N) :- % U is a list of unique elements
(not(member(X, U)), append(U, X, U2));
count(T, U2, N1),
N is N1 + 1.
%------ helpers ------
member_(X,[Y|T]):-
member_(X, T).
member_(X,[X|_]).
append([], Y, [Y]). % append[] and Y to get Y.
append([H|X], Y, [H|Z]) :- append(X, Y, Z). % append [H|X] and Y to get [H|Z] if appending X and Y gives Z
But running above simply return false:
3 ?- count([1,2,3],N).
Call: (10) count([1, 2, 3], _7714) ? creep
Fail: (10) count([1, 2, 3], _7714) ? creep
false.
Why is this so?
But running above simply return false:
3 ?- count([1,2,3],N).
Call: (10) count([1, 2, 3], _7714) ? creep
Fail: (10) count([1, 2, 3], _7714) ? creep
false.
Why is this so?
Because there is no head of a clause that count([1,2,3],N) can match.
You have two clauses for count/2:
count([],0). %%% clause 1
count([X,T], N) :- ... %%% clause 2
Since [1,2,3] can't be unified with [], clause 1 is no match.
But [1,2,3] can also not be unified with [X,T], thus clause 2 is also no match. Therefore, count([1,2,3],N) cannot be proven.
You can test this by asking
?- [X,T]=[1,2,3].
false.
What you need to recursively break down lists of arbitrary length is [X|T] instead. (Please test ?- [X|T]=[1,2,3]. to see how you could fix your code.)

Prolog unifies a variable with a term then forgets the unification

I have the following predicate which I have written for recognising when two lists are the same except the two elements at indices I1 and I2 are swapped:
swapped(I1, I2, List, NewList) :-
% The lists are the same length and the two indices are swapped.
same_length(List, NewList),
nth0(I1, List, V1), nth0(I2, List, V2),
nth0(I1, NewList, V2), nth0(I2, NewList, V1),
% All the other indices remain the same.
proper_length(List, Length), Lim is Length - 1,
numlist(0, Lim, Indices),
forall((member(I, Indices), I \= I1, I \= I2),
(nth0(I, List, V), nth0(I, NewList, V))).
The following swipl output demonstrates my issue:
?- swapped(0, 1, [1,2,3], L).
L = [2, 1, _G5035].
?- swapped(0, 1, [1,2,3], [2,1,3]).
true.
?- swapped(0, 1, [1,2,3], [2,1,4]).
false.
Why does it return a variable for the third element rather than just 3, given that it can recognise that 3 is the only correct term? These are the last four parts of the trace where the unification happens and is then forgotten:
Call: (10) lists:nth0(2, [2, 1, _G6121], 3) ? creep
Exit: (10) lists:nth0(2, [2, 1, 3], 3) ? creep
^ Exit: (8) forall(user: (member(_G6145, [0, 1, 2]), _G6145\=0, _G6145\=1), user: (nth0(_G6145, [1, 2, 3], _G6162), nth0(_G6145, [2, 1, _G6121], _G6162))) ? creep
Exit: (7) swapped(0, 1, [1, 2, 3], [2, 1, _G6121]) ? creep
I don't doubt there's a better way to swap two elements (perhaps recursively) but I would like to know why this is happening and how to fix it; I'm clearly lacking in some Prolog knowledge.
Thanks!
forall/2 is a so called 'failure driven loop'. Then instantiations are undone between cycles.
In SWI-Prolog, there is foreach/2, that fixes the problem with your first query.
...
numlist(0, Lim, Indices),
foreach((member(I, Indices), I \= I1, I \= I2),
(nth0(I, List, V), nth0(I, NewList, V))).
Test:
?- swapped(0, 1, [1,2,3], L).
L = [2, 1, 3].
In SWI-Prolog, sometime the better way to understand a builtin is to inspect the source. You can see that foreach/2 is a fairly complicated predicate... from swipl prompt, try ?- edit(foreach)., or follow the source link from the doc page (the circled :-).

Prolog unifying two lists

Alright, so I was trying to unify this two lists: [2] and [1,_,3] giving an answer of [1,2,3]. My code is below:
unify([],[],_).
unify(List1, [Head|Rest], List2) :- member(List1,Head),!,
unify([X|_], Rest, [X|List2]).
unify(List1, [Head|Rest], [Head|List2]) :- unify(List1, Rest, List2).
when i put ?-unify([2],[1,_,3],L) it gives me false, but I want it to give L=[1,2,3]. How can I improve the above code?
I tried a trace too but I couldn't figure out.
[debug] 5 ?- unify([2],[1,_,3],L).
T Call: (6) unify([2], [1, _G512, 3], _G520)
T Redo: (6) unify([2], [1, _G512, 3], _G520)
T Call: (7) unify([2], [_G512, 3], _G602)
T Call: (8) unify([_G607|_G608], [3], [_G607|_G602])
T Redo: (8) unify([_G607|_G608], [3], [_G607|_G602])
T Call: (9) unify([3|_G608], [], _G602)
T Fail: (9) unify([3|_G608], [], _G602)
T Fail: (8) unify([_G607|_G608], [3], [_G607|_G602])
T Fail: (7) unify([2], [_G512, 3], _G602)
T Fail: (6) unify([2], [1, _G512, 3], _G520)
false
First, you need to fix the base case: rather than using
unify([],[],_).
which says that an empty list on the left requires an empty list in the middle, and produces an undefined result, use these two:
unify(_,[],[]).
unify([],L,L).
The first base clause says that if the second list is empty, the output is empty as well, no matter what's in the first list.
The second base clause says that when the first list is empty, the output is the same as the second list.
Now we need to build two clauses that reduce the problem:
unify([H|T1], [H|T2], [H|R]) :- unify(T1, T2, R).
unify([H1|T1], [H2|T2], [H2|R]) :- H1 \= H2, unify([H1|T1], T2, R).
The first clause says that if the heads of the two lists unify, then attach the unified heads to the output of the reduced problem when both lists are reduced.
The second clause says that if the heads do not unify, then attach the head of the second list to the output of the reduced problem when only the second list is reduced.
Here is a demo on ideone.

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