List from nested predicates - prolog

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.

Related

Why this failed

I'm a beginner in Prolog after 40 years of imperative programming.
#!/usr/bin/swipl
:- use_module(library(clpfd)).
all_digits( [] ).
all_digits( [H|T] ) :-
memberchk( H, [0,1,2,3,4,5,6,7,8,9] ),
all_digits( T ).
:-
is_list( A ),
length( A, 3 ),
all_digits( A ),
halt( 0 ).
This program is in progress, I have another rules to add, but it already failed.
Why?
I expect 720 solutions: n!(n−p)! because each digit must be used only once.
is_list( A ) fails since A is not a list when the predicate is called. You can remove it. Moreover, you do not need, in this snippet, the clpfd library. Finally, note that memberchk/2, after a success, cuts all the remaining solutions. If you want to generate all the solutions, you should use member/2.
Moreover, you define a list of three elements, and each one can be a number between 0 and 9. So you will get 10*3 = 1000 solutions, instead of the 720 you expect.
The query fails because of is_list/1 since A is a variable.
Using clpfd, you can replace all_digits(A) with A ins 0..9. The list is of length 3 and each element can have 10 possible values if the value of A is enumerated then there should be 1000 solutions.
The query rewritten:
:- use_module(library(clpfd)).
?- length(Vs, 3), Vs ins 0..9, all_distinct(Vs), label(Vs).
The predicate all_distinct/1 is used to constraint each variable of Vs to be distinct and the predicate label/1 is used to find a solution.
Without using clpfd:
uniq_digits_3(L) :-
% Assemble list of [0, 1, ..., 9]
numlist(0, 9, Digits),
% Restrict length, so selects/2 knows when to stop
length(L, 3),
selects(L, Digits).
selects([], _Ys).
selects([X|Xs], Ys) :-
% Take 1 element from Ys
select(X, Ys, Ys0),
% Loop with the remaining Ys0
selects(Xs, Ys0).
Result in swi-prolog:
?- bagof(L, uniq_digits_3(L), Ls), length(Ls, LsLen).
Ls = [[0, 1, 2], [0, 1, 3], ...
LsLen = 720.

Unexpected behavior of this prolog program

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.

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: sorting w.r.t. some attribute

My database is similar to this:
% happy(Person,Happiness)
happy(1,10).
happy(2,5).
happy(3,8).
happy(4,1).
I want to sort people w.r.t. their happiness.
I coded the following and it does what I want. However it looked cumbersome to me. Any improvements?
? - sortPeople(Ts).
Ts = [1, 3, 2, 4].
My solution:
getFirst([],R,R).
getFirst([[H1,_]|T],F,R) :-
append([H1],F,R1),
getFirst(T,R1,R).
compareHappiness(X, [_,S1], [_,S2]) :- compare(X, S1, S2).
sortPeople(Ts) :-
findall([X,Y], happy(X,Y), List),
predsort(compareHappiness, List, SortedList),
getFirst(SortedList,[],Ts).
Consider using more descriptive and declarative predicate names, for example:
person_happiness(1, 10).
person_happiness(2, 5).
person_happiness(3, 8).
person_happiness(4, 1).
To sort people by happiness, consider using the built-in keysort/2, which is more efficient than predsort/3. You only need to build key-value pairs, for which by convention the functor -/2 is used, and instead of your auxiliary predicate, consider using the SWI-Prolog built-ins pairs_values/2 and reverse/2:
descending_happiness(Ps) :-
findall(H-P, person_happiness(P, H), HPs),
keysort(HPs, HPs1),
pairs_values(HPs1, Ps1),
reverse(Ps1, Ps).
Example query:
?- descending_happiness(Ps).
Ps = [1, 3, 2, 4].
-Here is what I got :
sort(Rez) :- findall([Happiness,PId],happy(PId,Happiness),List),
msort(List,LSorted),
findall(PersonID,member([_,PersonID],LSorted),Sorted),
reverse(Sorted,Rez).

Predicate to filter non constants from a list

I'm working on a predicate only_atoms/2(List+, Result-) that I'd like to filter non atoms.
For example :
only_atoms([1, 2, X, h(Y), 'aba'], Result).
should return
Result = [1, 2, 'aba'].
I do not care about the order.
Here is the piece of code I came up with :
only_atoms([], []) :- !.
only_atoms([Head | Tail], [Head | Result]) :-
atom(Head),
!,
only_atoms(Tail, Result).
only_atoms([_ | Tail], Result) :-
only_atoms(Tail, Result).
I thought this was the right kind of reasoning to handle such a problem but seem to be wrong since it yields me [](edit : it actually yields [aba], see precisions below, my bad !) no matter what. I'd appreciate some help !
A first hint: for 1 and 2, atom returns false.
By the way, I was looking for the filter predicate, in the standard library it happens to be called include, it's usually better if you use what the language already provides ;-)
?- include(atom, [1, 2, X, h(Y), 'aba'], Result).
Result = [aba].
or if you wanted just to filter out variables:
?- exclude(var, [1, 2, X, h(Y), 'aba'], Result).
Result = [1, 2, h(Y), aba].
Another by the way, one curious difference between your only_atoms and using include(atom, ...) is that yours will unify variables in the first list with atoms in the second list, whereas the include won't.
?- only_atoms([1, x, 2, Y], [x, y]).
Y = y.
?- include(atom, [1, x, 2, Y], [x, y]).
false.
Those subtleties of Prolog always astonish me (I guess that's because I didn't pay enough attention at the university xD).
You probably need to force the Head not to be an atom on the alternate clause, otherwise it is an option for atoms as well.
This returns Result = ['aba'] for me.
only_atoms([], []).
only_atoms([Head | Tail], [Head | Result]) :- atom(Head), !, only_atoms(Tail, Result).
only_atoms([Head | Tail], Result) :- \+atom(Head), !, only_atoms(Tail, Result).
Alternatively, you could try using findall/3.
atoms_list(List, Result) :- findall(Item, (member(Item, List), atom(Item)), Result).

Resources