How to get a list of possible predicate values - prolog

Lets I have a predicate p/1 defined, for example, as follows:
p(2).
p(3).
p(5).
p(7).
How can I define a predicate p_list/1 which will be true for a list of all possible values of p/1 (in above case - [2, 3, 5, 7]) in the backtracking order?
Simple enumeration of the values is not acceptable because it makes maintenance more difficult. Moreover values, can be defined implicitly.

You can use bagof(X, p(X), L) which gives you L = [2,3,5,7].
What do you mean by "defined implicitly" ? Can you give an example.

maplist/2 works well for check, while findall/3 it's the basic list constructor in Prolog
Try
?- findall(X, p(X), L), maplist(p, L).

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) the first solution from the list that satisfies the goal

I need to find the first element on the list which satisfies a user provided goal. I mean something like maplist/2 but which succeeds when the goal can be applied to at least one element. include/3 would be an option but I'm after a more optimal solution that can stop after finding the first element.
I think the interface could look like:
first(:Goal, List, First)
and it should be semidet in the SWI-Prolog sense.
It's fairly easy to code it but I'd prefer an existing rule. Is there a relevant rule in the "standard" libraries; I'm using SWI-Prolog.
Cheers,
Jacek
I don't think there's a standard predicate that does this. But as you say, it would be very easy to code. I would probably write something like this, which has a pattern like the member/2 predicate:
includes_item(Goal, [X|_], X) :-
call(Goal, X).
includes_item(Goal, [_|T], X) :-
includes_item(Goal, T, X).
As #false indicates in the comments, this in fact can be more clearly written using member/2:
includes_item(Goal, List, Item) :-
member(Item, List),
call(Goal, Item).
includes_item(:Goal, List, Item) succeeds for each Item in List that satisfies :Goal. For example:
3 ?- includes_item('>'(3), [1,2,3,-2, 4, 5], X).
X = 1 ;
X = 2 ;
X = -2 ;
false.
You can then use once/1 to obtain only the first item without a choice point:
first(Goal, List, Item) :-
once(includes_item(Goal, List, Item)).
And now you get:
4 ?- first('>'(3), [1,2,3,-2, 4, 5], X).
X = 1.
5 ?-

How to check which items on the list meet certain condition?

How to make a function called busLineLonger, which receives at least two parameters to decide if a bus line is longer or not?
*/This is how it works*/
* busStops(number_of_the_bus,number_of_stops)*/
/*?- busLineLonger([busStops(1,7),busStops(2,4),busStops(3,6)],5,WHICH).
* WHICH = [1,3].
Using only comparative things, like #> <# /==#.
Sorry my english
Edit...
So far I've think of something like this
busLineLonger([busStops(A,B)|R],N,[_|_]):-
N#>B,
busLineLonger(R,N,A).
Here's how you could do it using meta-predicates,
reified test predicates,
and lambda expressions.
:- use_module(library(lambda)).
First, we define the reified test predicate (>)/3 like this:
>(X,Y,Truth) :- (X > Y -> Truth=true ; Truth=false).
Next, we define three different implementations of busLineLonger/3 (named busLineLonger1/3, busLineLonger2/3, and busLineLonger3/3) in terms of the following meta-predicates: maplist/3, tfilter/3, tfiltermap/4, and tchoose/3. Of course, in the end we will only need one---but that shouldn't keep us from exploring the various options we have!
#1: based on tfilter/3 and maplist/3
Do two separate steps:
1. Select items of concern.
2. Project those items to the data of interest.
busLineLonger1(Ls0,N,IDs) :-
tfilter(\busStops(_,L)^(L>N), Ls0,Ls1),
maplist(\busStops(Id,_)^Id^true, Ls1, IDs).
#2: based on tfiltermap/4
Here, we use exactly the same lambda expressions as before, but we pass
them both to meta-predicate tfiltermap/4. Doing so can help reduce
save some resources.
busLineLonger2(Ls,N,IDs) :-
tfiltermap(\busStops(_,L)^(L>N), \busStops(Id,_)^Id^true, Ls,IDs).
Here's how tfiltermap/4 can be implemented:
:- meta_predicate tfiltermap(2,2,?,?).
tfiltermap(Filter_2,Map_2,Xs,Ys) :-
list_tfilter_map_list(Xs,Filter_2,Map_2,Ys).
:- meta_predicate list_tfilter_map_list(?,2,2,?).
list_tfilter_map_list([],_,_,[]).
list_tfilter_map_list([X|Xs],Filter_2,Map_2,Ys1) :-
if_(call(Filter_2,X), (call(Map_2,X,Y),Ys1=[Y|Ys0]), Ys1=Ys0),
list_tfilter_map_list(Xs,Filter_2,Map_2,Ys0).
#3: based on tchoose/3
Here we do not use two separate lambda expressions, but a combined one.
busLineLonger3(Ls,N,IDs) :-
tchoose(\busStops(Id,L)^Id^(L>N), Ls,IDs).
Here's how tchoose/3 can be implemented:
:- meta_predicate tchoose(3,?,?).
tchoose(P_3,Xs,Ys) :-
list_tchoose_list(Xs,P_3,Ys).
:- meta_predicate list_tchoose_list(?,3,?).
list_tchoose_list([],_,[]).
list_tchoose_list([X|Xs],P_3,Ys1) :-
if_(call(P_3,X,Y), Ys1=[Y|Ys0], Ys1=Ys0),
list_tchoose_list(Xs,P_3,Ys0).
Let's see them in action!
?- Xs = [busStops(1,7),busStops(2,4),busStops(3,6)], busLineLonger1(Xs,5,Zs).
Xs = [busStops(1, 7), busStops(2, 4), busStops(3, 6)],
Zs = [1, 3].
?- Xs = [busStops(1,7),busStops(2,4),busStops(3,6)], busLineLonger2(Xs,5,Zs).
Xs = [busStops(1, 7), busStops(2, 4), busStops(3, 6)],
Zs = [1, 3].
?- Xs = [busStops(1,7),busStops(2,4),busStops(3,6)], busLineLonger3(Xs,5,Zs).
Xs = [busStops(1, 7), busStops(2, 4), busStops(3, 6)],
Zs = [1, 3].
Done!
So... what's the bottom line?
Many meta-predicates are versatile and can be used in a lot of sitations similar to the one here.
Implementing these meta-predicates is a one time effort that is amortized quickly.
Many meta-predicates handle the "recursive part", which enables you to focus on actual work.
Often, with meta-predicates (as with regular ones), "there's more than one way to do things".
Depending on the concrete circumstances, using a particular meta-predicate may be better than using another one, and vice versa.
For this question, I think, implementation #3 (the one using tchoose/3) is best.
Some things to fix in your code:
3rd argument is [_|_], that is the result are free variables... doesn't make sense. You need two cases: one in which the B is greater than N and you include the result; the other in which B is less or equal than N, and you don't include that result.
base case is missing. what's the result when bus list is empty?
A possible solution:
busLineLonger([],_,[]).
busLineLonger([busStops(A,B)|R],N,[A|S]) :- B>N, busLineLonger(R,N,S).
busLineLonger([busStops(_,B)|R],N,S) :- B=<N, busLineLonger(R,N,S).
?- busLineLonger([busStops(1,7),busStops(2,4),busStops(3,6)],5,WHICH).
WHICH = [1, 3]

Prolog List Squaring, Modifying element in List

I am trying to write a short Prolog program which takes a list of numbers and returns a list where all numbers have been squared.
Ie: [2,4,5] => [4,16,25]
My code so far:
list_of_squares([X], L) :-
L is X^2.
list_of_squares([X|XS], [X^2|M]) :-
list_of_squares(XS, M).
For some reason though Prolog doesn't like me squaring X while adding it to a list... Any thoughts on how I could do this?
You're not that far off, but you make two small mistakes:
Firstly, you mix element X with list L. Your first clause should be:
list_of_squares([X], [Y]):-
Y is X ^ 2.
Secondly, you cannot perform an arithmetic function in list notation.
Your second clauses should be as follows:
list_of_squares([X|Xs], [Y|Ys]):-
Y is X ^ 2,
list_of_squares(Xs, Ys).
Thirdly, there is a more fundamental problem. With the first two fixes, your code works, but the base case, i.e. the first clause, is not that well chosen. (A) the code cannot process the empty list. (B) For a singleton list the code is needlessly nondeterministic, because both clauses apply. This is solved by choosing the base case wisely:
squares([], []).
squares([X|Xs], [Y|Ys]):-
Y is X ^ 2,
squares(Xs, Ys).
Here is a general method how you can localize such an error. First, let's start with your exemple:
?- list_of_squares([2,4,5],[4,16,25]).
false.
Oh no! It fails! There is a very general method what to do in such a situation:
Generalize the query
So we replace [4,16,25] by a new, fresh (ah, true freshness!) variable:
?- list_of_squares([2,4,5],L).
L = [2^2,4^2|25]
; false.
That's way better: Now you know that there is a "result", but that result it not what you expected.
Next,
Minimize the query
The list is way too long, so I will chop off some elements. Say, the first two:
?- list_of_squares([5],L).
L = 25
; false.
Again, wrong, but smaller. Now, where is the error for that? To get it
Specialize your program
list_of_squares([X], L) :-
L is X^2.
list_of_squares([X|XS], [X^2|M]) :- false,
list_of_squares(XS, M).
That program, again gives the same wrong answer! So in there is a bug in the visible part. What we expect is
?- list_of_squares([5],[25]).
false.
this to succeed. But where is the error? Again:
Generalize the query
?- list_of_squares([5],[X]).
false.
HET!
Now, you should realize that that rule might be:
list_of_squares([X], [Y]):-
Y is X ^ 2.
And the same (is)/2 should be used in the recursive rule. And, why not accept [].
I, personally, would rather write using library(lambda):
list_of_squares(Xs, Ys) :-
maplist(\X^XX^(XX is X^2), Xs, Ys).
Or, even better, using library(clpfd)
list_of_squares(Xs, Ys) :-
maplist(\X^XX^(XX #= X^2), Xs, Ys).
Prolog doesn't have a 'functional' mindset, but some standard builtin predicate can help working with lists. In this case
list_of_squares(L,S) :- findall(Sq,(member(E,L),Sq is E*E),S).
?- list_of_squares([2,4,5], R).
R = [4, 16, 25].
in this case, member/2 play a role similar to lambda expressions, giving a 'name' to each element E available in L. findall/3 compute all solutions of its goal ,(member(E,L),Sq is E*E),, and collects results (the 'template' expression, that is, Sq).

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

Resources