It is possible to manipulate a list generated with findall? - list

The context is the following in my program: I have some students, of a certain country and with certain years of study, as follows in this prolog code:
student('Steve Morris').
student('Joe Jez').
student('Carlos Sethi').
student('Natasha Carter').
country('Steve Morris', usa).
country('Joe Jez', usa).
country('Carlos Sethi', usa).
country('Natasha Carter', france).
years('Steve Morris', 3).
years('Joe Jez', 1).
years('Carlos Sethi', 4).
years('Natasha Carter', 4).
scholarship(A) :- country(A,B), B = france.
scholarship(A) :- years(A,C), C > 2.
I want to give one and just one scholarship to one of my students. To do this, I will use some rules that will raise the "scholarship factor", and the student who gets the greater scholarship factor will get the scholarship.
The first rule states that the student must come from France, and the second rule states that the student must have more than two years of study.
So, when I execute scholarship(X), this is what I get:
?- scholarship(X).
X = 'Natasha Carter' ; % Only student who matches the first rule
X = 'Steve Morris' ; % All students from now on, match the second rule
X = 'Carlos Sethi' ;
X = 'Natasha Carter'.
With that said, I'm trying to do a program that tries to get the name of the student who finally gets the scholarship. To start, I've tried first to execute a predicate findall to filter all the student that match these rules one by one, and put it on a list:
?- findall(X, scholarship(X), L).
L = ['Natasha Carter', 'Steve Morris', 'Carlos Sethi', 'Natasha Carter'].
Which is an expected result, because of the use of scholarship(X).
Now, looks like I will need the generated list to filter the results and satisfy what I'm looking for. Take in mind that in the example above, the result that I'm expecting to reach, at least, is a list which indicates the student and his scholarship factor, something like this (not necessarily exact):
[['Natasha Carter', 2], ['Steve Morris', 1], ['Carlos Sethi', 1]].
It is a way to manipulate the list generated with findall? Or I certainly need another way to solve this?
Edit: There's something important about the problem modeling: All rules have the same scholarship factor value, so when a student satisfy a rule, no matter which one, the scholarship factor should rise up to 1.
Updates to the problem: Thanks to Mog I have an approach of the problem, applying msort/2 using a secondary list, this is what I've got:
?- findall(X, scholarship(X), L), msort(L, L1).
L = ['Natasha Carter', 'Steve Morris', 'Carlos Sethi', 'Natasha Carter'],
L1 = ['Carlos Sethi', 'Natasha Carter', 'Natasha Carter', 'Steve Morris'].

the simpler way should be to augment scholarship(X) with a rank (I've added the rule 'index'), then use setof to get a sorted list, from lower to higher ranking:
scholarship(1,A) :- country(A,B), B = france.
scholarship(2,A) :- years(A,C), C > 2.
?- setof(R-X, scholarship(R,X), L).
note I changed the list' elements to a more useful format, exploiting the sorting that setof perform.
edit: sorry, I suggested an useless modification. A more appropriate answer would suggest
?- findall(N-X, (bagof(_, scholarship(X), T), length(T, N)), L).
L = [1-'Carlos Sethi', 2-'Natasha Carter', 1-'Steve Morris'].

I've found myself an alternate solution by the use of a registry structure called:
reg(C, N)
where C is the name of the person, and N is the number of times the name of the person appears on the list generated with findall (The use of msort/2, for effects of this solution, it's just for order purposes).
listsort(L1) :- findall(X, scholarship(X), L), msort(L, L1).
compress([],[]).
compress([X|Xs],Ys):-comp(Xs,X,1,Ys).
comp([],C,N,[reg(C,N)]).
comp([X|Xs],X,N,Ys):-N1 is N+1, comp(Xs,X,N1,Ys).
comp([X|Xs],Y,N,[reg(Y,N)|Ys]):- X\=Y, comp(Xs,X,1,Ys).
predic(S2) :- listsort(S1), compress(S1, S2).
So the output for the original problem looks similar to the proposed one (the only difference is that in the original one the registries are ordered by N:
?- predic(K).
K = [reg('Carlos Sethi', 1), reg('Natasha Carter', 2), reg('Steve Morris', 1)]

Related

Prolog: compare list elements and sum

New to prolog and trying to implement the following function that takes 3 lists:
True if lists are the same length
True if elements of third list is sum of the two lists
Example: fn([1,2,3],[4,5,6],[5,7,9]) returns true. Note that the sum is element-wise addition.
This is what I have so far:
fn([],[],[]).
fn([_|T1], [_|T2], [_|T3]) :-
fn(T1,T2,T3), % check they are same length
fn(T1,T2,N1), % check that T3=T1+T2
N1 is T1+T2,
N1 = T3.
From what I understand, the error is due to the base case (it has empty lists which causes error with evaluation of addition?)
Thanks for any help and explanations!
In addition to #GuyCoder's answer, I would point out that it is worthwhile to consider using one of the maplist predicates from library(apply) when modifying all elements of lists. You can use a predicate to describe the relation between three numbers...
:- use_module(library(apply)). % for maplist/4
num_num_sum(X,Y,S) :-
S is X+Y.
... and subsequently use maplist/4 to apply it to entire lists:
fn(X,Y,Z) :-
maplist(num_num_sum,X,Y,Z).
This predicate yields the desired results if the first two lists are fully instantiated:
?- fn([1,2,3],[4,5,6],X).
X = [5,7,9]
However, due to the use of is/2 you get instantiation errors if the first two lists contain variables:
?- fn([1,A,3],[4,5,6],[5,7,9]).
ERROR at clause 1 of user:num_num_sum/3 !!
INSTANTIATION ERROR- X is _+B: expected bound value
?- fn([1,2,3],[4,5,A],[5,7,9]).
ERROR at clause 1 of user:num_num_sum/3 !!
INSTANTIATION ERROR- X is A+B: expected bound value
If you only want to use the predicate for lists of integers, you can use CLP(FD) to make it more versatile:
:- use_module(library(apply)).
:- use_module(library(clpfd)). % <- use CLP(FD)
int_int_sum(X,Y,S) :-
S #= X+Y. % use CLP(FD) constraint #=/2 instead of is/2
fnCLP(X,Y,Z) :-
maplist(int_int_sum,X,Y,Z).
With this definition the previously problematic queries work as well:
?- fnCLP([1,A,3],[4,5,6],[5,7,9]).
A = 2
?- fnCLP([1,2,3],[4,5,A],[5,7,9]).
A = 6
Even the most general query yields results with this version:
?- fnCLP(X,Y,Z).
X = Y = Z = [] ? ;
X = [_A],
Y = [_B],
Z = [_C],
_A+_B#=_C ? ;
X = [_A,_B],
Y = [_C,_D],
Z = [_E,_F],
_A+_C#=_E,
_B+_D#=_F ? ;
.
.
.
Since the numbers in the above answers are not uniquely determined, you get residual goals instead of actual numbers. In order to get actual numbers in the answers, you have to restrict the range of two of the lists and label them subsequently (see documentation for details), e.g. to generate lists containing the numbers 3,4,5 in the first list and 6,7,8 in the second list, you can query:
label the lists
restrict the domain | |
v v v v
?- fnCLP(X,Y,Z), X ins 3..5, Y ins 6..8, label(X), label(Y).
X = Y = Z = [] ? ;
X = [3],
Y = [6],
Z = [9] ? ;
X = [3],
Y = [7],
Z = [10] ? ;
.
.
.
X = [3,4],
Y = [6,7],
Z = [9,11] ? ;
X = [3,4],
Y = [6,8],
Z = [9,12] ? ;
.
.
.
On an additional note: there are also clp libraries for booleans (CLP(B)), rationals and reals (CLP(Q,R)) that you might find interesting.
From what I understand, the error is due to the base case.
I don't see it that way.
The first problem I see is that you are trying to process list which leads to thinking about using DCGs, but since you are new I will avoid that route.
When processing list you typically process the head of the list then pass the tail back to the predicate using recursion.
e.g. for length of list you would have
ln([],N,N).
ln([_|T],N0,N) :-
N1 is N0+1,
ln(T,N1,N).
ln(L,N) :-
ln(L,0,N).
The predicate ln/2 is used to set up the initial count of 0 and the predicate ln/3 does the work using recursion. Notice how the head of the list is taken off the front of the list and the tail of the list is passed recursively onto the predicate again. When the list is empty the predicate ln([],N,N). unifies, in this case think copies, the intermediate count from the second position into the third position, which it what is passed back with ln/2.
Now back to your problem.
The base case is fine
fn([],[],[]).
There are three list and for each one look at the list as [H|T]
fn([H1|T1],[H2|T2],[H3|T3])
and the call to do the recursion on the tail is
fn(T1,T2,T3)
all that is left is to process the heads which is
H3 is H1 + H2
putting it all together gives us
fn([],[],[]).
fn([H1|T1], [H2|T2], [H3|T3]) :-
H3 is H1 + H2,
fn(T1,T2,T3).
and a quick few checks.
?- fn([],[],[]).
true.
?- fn([1],[1],[2]).
true.
?- fn([1,2],[3,4],[4,6]).
true.
?- fn([1,2],[3,4,5],[4,6,5]).
false.
With regards to the two conditions. When I look at exercises problems for logic programming they sometimes give a condition like True if lists are the same length or some other condition that returns true. I tend to ignore those at first and concentrate on getting the other part done first, in this case elements of third list is sum of the two lists then I check to see if the other conditions are correct. For most simple classroom exercises they are. I sometimes think teacher try to give out these extra conditions to confuse the student, but in reality the are there just to clarify how the code should work.

List - counting atoms related to their previous term

I want to count the number of elements in a list which have a relation with the element following.
The predicate I have works by using an accumulator variable which it increments if the predicate related returns true.
The following example code is to check the number of times an element is greater than it's previous element.
So for example
count_list([1,2,3,2,1,3,2],Count).
should return 3.
The code almost works. It increments the accumulator variable correctly. However, the function returns false, when it tries to compare the final 2 at the end with the non-existent next term.
listofitems([],N,N).
%count number of items which are related to the previous
listofitems([A,B|T],Acc,N) :-
write(A),write(' '), write(B),
( related(A,B) -> Acc1 is Acc+1 ; Acc1 = Acc ),
write(Acc1),write('\n'),
listofitems([B|T],Acc1,N).
count_list(L,N):-
listofitems(L,0,N).
%define the relationship to be counted
related(A,B):-
B>A.
Does anyone have any suggestions as to how to create an elegant terminating condition so I can return the accumulated value?
Does anyone have any suggestions as to how to create an elegant terminating condition so I can return the accumulated value?
The problem you have is that your query fails. Try first to minimize the query as much as possible. Certainly, you expect it to work for:
?- listofitems([], Count).
Count = 0.
Yet, it already fails for:
?- listofitems([1], Count).
false.
So let's try to dig into the reason for that.
And since your program is pure (apart from those writes), it is possible to diagnose this a little better by considering a generalization of your program. I prefer to look at such generalizations as I do not want to read too much (eye strain and such):
:- op(950, fy, *).
*_.
listofitems([], N,N).
listofitems([A,B|T], Acc,N) :-
* ( related(A,B) -> Acc1 is Acc+1 ; Acc1 = Acc ),
* listofitems([B|T], Acc1,N).
count_list(L,N):-
listofitems(L,0,N).
?- count_list([1], Count).
false.
Even this generalization fails! So now in desperation I try to ask the most general query. It's like when I ask one thing after the other and get a noe after a no. Good this is Prolog, for we can ask: "Say me just everything you know".
?- count_list(Es,Count).
Es = [], Count = 0
; Es = [_,_|_].
So it is only the case for the empty list and lists with at least two elements. But there is no answer for one-elemented lists! You will thus have to generalize the program somehow.
A natural way would be to add a fact
listofitems([_], N, N).
As a minor remark, this isn't called a "terminating condition" but rather a "base case".
And if you really want to trace your code, I recommend these techniques instead of adding manual writes. They are much too prone to error.
If the all list items are integers and your Prolog system supports clpfd, you can proceed like this:
:- use_module(library(clpfd)).
:- use_module(library(lists), [last/3]).
:- use_module(library(maplist), [maplist/4]).
To relate adjacent items, look at two sublists of [E|Es], Es and Fs. If, say,
[E|Es] = [1,2,3,2,1,3,2] holds ...
... then Fs lacks the last item (Fs = [1,2,3,2,1,3,2]) ...
... and Es lacks the first item (Es = [1,2,3,2,1,3,2]).
maplist/4 and i0_i1_gt01/3 map corresponding list items in Fs and Es to 0 / 1:
i_j_gt01(I, J, B) :- % if I #< J then B #= 1
I #< J #<=> B. % if I #>= J then B #= 0
?- maplist(i_j_gt01, [1,2,3,2,1,3], [2,3,2,1,3,2], Bs).
Bs = [1,1,0,0,1,0].
Last, sum up [1,1,0,0,1,0] using sum/3:
?- sum([1,1,0,0,1,0], #=, N).
N = 3.
Let's put it all together!
count_adj_gt([E|Es], N) :-
last(Fs, _, [E|Es]), % or: `append(Fs, [_], [E|Es])`
% or: `list_butlast([E|Es], Fs)`
maplist(i_j_gt01, Es, Fs, Bs),
sum(Bs, #=, N).
Sample query using SICStus Prolog 4.3.2:
?- count_adj_gt([1,2,3,2,1,3,2], N).
N = 3. % succeeds deterministically
not sure about
an elegant terminating condition
my whole code would be
?- Vs=[1,2,3,2,1,3,2], aggregate_all(count, (append(_,[X,Y|_], Vs), X<Y), Count).
That's all...
If you need something more complex, remember that library(clpfd) has more to offer.

Using Pattern Matching in Prolog to Find Submultisets

I am new to prolog and I was wondering if anyone could help me with this problem. The problem: given the integers 1,2,3,4, and the predicates mult/2, div/2, div/2, minus/2, and minus/2, and eval/2, I need to write a predicate solutions/1 that, when called like this:
?- solutions(L).
it should terminate with the variable L unified to a list of expressions with value 6. Expressions are of the form:
X, Y, exp/2
But my code is not working. I have two versions. The first freezes up SWI-Prolog, not returning any answer after I type a period, and not letting me evaluate anything else afterward:
eval(1,1.0).
eval(2,2.0).
eval(3,3.0).
eval(4,4.0).
eval(mult(X,Y),Z) :-
eval(X,A),
eval(Y,B),
Z is A*B.
eval(div(X,Y),Z) :-
eval(X,A),
eval(Y,B),
Z is A/B.
eval(minus(X,Y),Z) :-
eval(X,A),
eval(Y,B),
Z is A-B.
solutions(L) :-
setof(X,eval(X,6),L),
print(L).
The second version just returns false when I type ?- solutions(L).:
solutions(L) :-
setof([exp,X,Y],eval(exp(X,Y),6),L),
print(L).
Thank you so much for taking the time to help!
Maybe you're looking for something like
solutions(L) :-
Ns = [1,2,3,4],
Ex = [*,/,-],
findall((X,Y,E),
(member(X,Ns),member(Y,Ns),member(E,Ex),F=..[E,X,Y],6=:=F),
L).
that yields
?- solutions(L).
L = [(2, 3, (*)), (3, 2, (*))].
Expressions are usually recursive, that is, arguments could be expressions instead of plain numbers. But then, in my opinion your problem is underspecified, as we need criteria to stop the infinite flow of solutions resulting - for instance - by repeated application of operations that don't change the value. Like multiply or divide by 1.
The problem is that your code is going in infinite recursion with eval/2 predicate.
You can try this solution:
num(1).
num(2).
num(3).
num(4).
eval(mult(A,B),Z) :-
num(A),
num(B),
Z is A*B.
eval(div(A,B),Z) :-
num(A),
num(B),
Z is A/B.
eval(minus(A,B),Z) :-
num(A),
num(B),
Z is A-B.
test(L) :-
setof(X,eval(X,6),L),
print(L).
Which yields:
?- test(L).
[mult(2,3),mult(3,2)]
L = [mult(2, 3), mult(3, 2)].

Prolog, permutation code understanding

I'm trying to understand how this program works.
Code from Daniel Lyons' solution(from the link above)
takeout(X,[X|R],R).
takeout(X,[F |R],[F|S]) :- takeout(X,R,S).
perm([X|Y],Z) :- perm(Y,W), takeout(X,Z,W).
perm([],[]).
I'm trying ti understand how it works with this list [1,2,3]
So, I have perm([1,2,3],X).
It's easy to understand at first, Y = [2,3] then Y = [3] and then Y = []
After that perm([],[]). is called and it gives us W = []
Now, takeout is called for the first time - takeout(3, Z, []).
It returns Z = [3]
Now, we are going back, where perm([],[]). gives us W = [3], (because Y was [3] at this point)
Same as above, takeout(2, Z, [3]) and Z = [2, 3].
Again perm([], []). and W = [2, 3].
And takeout(1, Z, [2, 3]), which gives us first answer Z = [1, 2, 3]
Here I don't know why program don't end , recursion is done, so why takeout and perm are working again ?
After that takeout is called takeout(1, [2,3]).
Which now works with takeout(X,[F |R],[F|S]) and not with takeout(X,[X|R],R). and that's my second question, why?
In Prolog, a predicate's behavior is quite unlike that of a function in procedural languages. A function is called to perform a task, it executes, and then comes back returning some values or having performed some side effects, or both.
A predicate defines a relation and/or set of facts that establish a logical connection between it's arguments. When a query is made to a predicate in Prolog, Prolog will attempt to find every instantiation of the argument variables that will make that predicate succeed (be true).
In a very simple case, I might have the following facts:
likes(tom, mary). % Tom likes Mary
likes(fred, mary). % Fred likes Mary
Here I have one predicate or fact, likes, which defines a relation between two people. We call the above facts because they each specify a precise, concrete relation with fully instantiated arguments. I can make a query to determine Who likes Mary? as follows:
| ?- likes(Person, mary).
Person = tom ? ;
Person = fred
yes
The query first comes back with Person = tom but indicates it has more options to check once it has found that Person = tom satisfies the query. Entering ; tells Prolog to continue with the next solution (if there is one), and it finds it: Person = fred.
Now let's consider takeout/3. This is a predicate which defines a relation between a set of variables.
takeout(X,[X|R],R).
takeout(X,[F|R],[F|S]) :- takeout(X,R,S).
The takeout/3 predicate has two predicate clauses or rules for the relation. It's helpful to try to read them:
R is what you get if you take X out of [X|R].
[F|S] is what you get if you take X out of [F|R] if S is what you get when you take X out of R.
Prolog looks at multiple clauses in a disjunctive way. That is, a query or call to the predicate will succeed if any one of the rules can hold true. When a query on takeout/3 is made, Prolog will look for instantiations of the given variables in the query which will make it true, and it will attempt to find every such instantiation that does so. In other words, if there's more than one way to satisfy the condition, it will backtrack and attempt to find those variables instantiations that do so.
Consider the query:
?- takeout(X, [1,2,3], R).
Prolog is able to match this to the first predicate clause: takeout(X, [X|R], R) as takeout(1, [1,2,3], [2,3]) by instantiating X = 1 and R = [2,3]. So this query will succeed with the following result:
R = [2,3]
X = 1 ?
But we see that Prolog is indicating there are more options to explore. That's because there's another clause: takeout(X,[F|R],[F|S]) which matches the query, takeout(X, [1,2,3], R). Prolog therefore backtracks and attempts the second clause, which matches:
takeout(X, [1|[2,3]], [1|S]) :- % F = 1, R = [2,3]
takeout(X, [2,3], S).
Prolog will then follow the recursive call takeout(X, [2,3], S) and start from the first clause again and attemp to match takeout(X, [2,3], S) with takeout(X, [X|R], R), which succeeds with X = 2 and S = [3] (takeout(2, [2|[3]], [3]).. The recursion unwinds or returns (as it would in any language), and the previous call head, takeout(X, [1|[2,3]], [1|S]) then ends up instantiating as: takeout(1, [1|[2,3]], [1|[3]]). So we get:
R = [2,3]
X = 1 ? ;
R = [1,3] % that is, [1|[3]]
X = 2 ?
And so on. Similar behavior applies to perm. In the context of the query perm, the calls to takeout backtrack to produce additional results, so perm produces additional results (since its calls to takeout backtrack, just like they do when you query takeout by hand).
As noted by #false, the predicate takeout/3 is implemented as a standard predicate in Prolog as select/3.

Find powers of 2 in a list Prolog

I'm trying to create a list in Prolog (SWI Prolog) and check which numbers are powers of 2 and second find how many times a specific number is in the list (in this example I'm trying to find how many times the number 3 is in the list).
For a example, if you ask
?- check([0,2,3,-5,-2,1,8,7,4], MULT2, THREE).
you should see
MULT2=[2,8,4]
THREE=1
My first try to find a solution is to search the list with head and doing head mod 2 = 0 to find all numbers which are powers of 2, but something went wrong and I only get "false" as an answer.
Here's how you can find the "powers of two" in logically-pure way!
Using sicstus-prolog 4.3.5, library(reif) and library(clpz):
:- use_module([library(reif), library(clpz)]).
power_of_two_t(I, T) :-
L #= min(I,1),
M #= I /\ (I-1),
call((L = 1, M = 0), T). % using (=)/3 and (',')/3 of library(reif)
Sample query1 using meta-predicate tfilter/3 in combination with power_of_two_t/2:
?- tfilter(power_of_two_t, [0,2,3,-5,-2,1,8,7,4], Ps).
Ps = [2,1,8,4]. % succeeds deterministically
Here's a more general query suggested by a comment:
?- tfilter(power_of_two_t, [X], Ps).
Ps = [X], 0#=X/\_A, _A+1#=X, X in 1..sup, _A in 0..sup
; Ps = [], dif(_A,0), _A#=X/\_B, _B+1#=X, X in 1..sup, _B in 0..sup
; Ps = [], dif(_A,1), _A#=min(X,1), _B#=X/\_C, _C+1#=X, X#>=_A, _A in inf..1.
Footnote 1: The answer sequences shown above were brushed up to indicate the determinism of calls.
Footnote 2: To reproduce the results use call_det/2 which is defined like this:
call_det(G_0, Det) :-
call_cleanup(G_0, Flag = set),
( nonvar(Flag)
-> Det = true
; Det = false
).
It's a strange thing to have two such a different tasks to do in one predicate. You should probably have two separate predicates, one for counting number of powers of 2 and one to count 3s. Then you can combine them in one predicate like:
check(Nums, MULT2, THREE) :-
count2powers(Nums, MULT2),
count3s(Nums, THREE).
After that you can decompose further and have a separate predicate to check if a number is a power of 2:
is2power(1).
is2power(N) :-
N > 0,
N2 is N // 2,
N2 * 2 =:= N,
is2power(N2).
This is basic software engineering and this way you can build your program step by step and you will be able to ask more concrete and meaningful questions than just "The whole program returns false."