Calculations in lists in PROLOG - prolog

I try to create an emergency system to find people on time and those late.
so my list now looks like:
[ [patient(204,4,2),patient(203,3,2)] , [patient(303,7,3),patient(302,6,3)] , [patient(404,12,4) ,patient(403,11,4)] , [patient(504,16,5),patient(503,15,5)] ]
I want for each list to find those on time using the following function (score):
(consultation time) * (index of patient) + (2nd argument of patient predicate)
consultation time is constant:
example :
consultTime(15)
For this exemple i have to get something like :
[ [4 , 18] , [37, 51] , [72, 86] , [106, 120] ]
the problem is with the indices how can i preserve the same index as with a normal list i.e.:
[ [0, 1], [2, 3], [4, 5] ]
I'm sorry, i didn't find a good question for my problem.

the problem is with the indices how can i preserve the same index as with a normal list i.e.:
[ [0, 1], [2, 3], [4, 5] ]
Pass a counter in at the start of each list, use it to count through the elements, then return how far it got. Pass that value in to start the count for the next list:
% process a single list, just showing the counter
process([], C, C, []).
process([P1|P1s], Count, CountTo, [Count|P2s]) :-
succ(Count, Next),
process(P1s, Next, CountTo, P2s).
% for the list of lists
test([], _, []).
test([L1|L1s], CountFrom, [L2|L2s]) :-
process(L1, CountFrom, CountTo, L2),
test(L1s, CountTo, L2s).
e.g.
?- _Patients = [
[patient(204,4,2), patient(203,3,2)],
[patient(303,7,3), patient(302,6,3)],
[patient(404,12,4), patient(403,11,4)],
[patient(504,16,5), patient(503,15,5)]
],
test(_Patients, 0, Ps).
Ps = [[0, 1], [2, 3], [4, 5], [6, 7]]

Related

Reversing matrix rows in Prolog

I am trying to write a predicate which reverses all rows in a matrix using Prolog.
For instance the matrix [ [1,2,3], [4,5,6], [7,8,9] ] should become [ [3,2,1], [6,5,4], [9,8,7] ].
My attempt so far is :
reverseRows([[H|T]|_],X):-reverse([H|T],X).
which reverses only the first row. How can I change the above to work for all rows ? Any assistance is welcome.
The simplest solution is to use the predicate maplist/3.
?- maplist(reverse, [[1,2,3], [4,5,6], [7,8,9]], Reversed).
Reversed = [[3, 2, 1], [6, 5, 4], [9, 8, 7]].
Other possible solution is:
reverse_rows([], []).
reverse_rows([Xs|Xss], [Ys|Yss]) :-
reverse(Xs, Ys),
reverse_rows(Xss, Yss).
Example:
?- reverse_rows([[1,2,3], [4,5,6], [7,8,9]], Reversed).
Reversed = [[3, 2, 1], [6, 5, 4], [9, 8, 7]].

Using maplist/2,3,4

I'm doing a Prolog project for uni that's based on the Kakuro game.
I'm having trouble with the predicate spaces_puzzle/2 that does the following:
Receives the Puzzle in the form of a list of lists that represents each file of the puzzle:
[[[sum_vertical, sum_horizontal], P11, P12, [sum_vertical, sum_horizontal],P13, P14...],
[[sum_vertical, sum_horizontal], P21, P22, P23, P24, ...],
[[sum_vertical, sum_horizontal], P31, P32, P33, P34, P35 ...],
...]
and returns a list like this [space(sum_horizontal, [P11,P12]), space(sum_horizontal, [P13,P14]), space(sum_horizontal, [P21,P22,P23,P24]) ...]
I was thinking of using a maplist with a predicate that I have already defined as spaces_in_file/3 which will read a list like [[sum_vertical, sum_horizontal], P11, P12, [sum_vertical, sum_horizontal],P13, P14...] and output in the wanted formatting: [space(sum_horizontal, [P11,P12]), space(sum_horizontal, [P13,P14])...]
I don't understand the syntax for the predicate. The "Goal" I want is the spaces_in_file/3. spaces_in_file(H_V, File, Spaces) is a simple bagof where H_V is a character that says which of the sums I should use, File is the file I want to check for the spaces, and Spaces is my output.
So should it be something like this:
spaces_puzzle(Puzzle, Spaces):-
maplist(spaces_in_file(h, File, Spaces), Puzzle, File)
I want my file to be one of the lists of the Puzzle. I can't seem to get the syntax right.
For now I just want the correct Result for the files, I can easily adapt to get the columns with the transpose/2 predicate which is suggested by the professors.
Appreciate any advice!
maplist/N takes a functor and some lists, and checks whether for each element in each list, the predicate named as that functor and with arguments from those lists, is true (you can fix some arguments by adding them to the functor), so e.g:
maplist( append, [[1], [1, 2], [1, 2, 3]], [[2, 4], [5], [6, 7]], List).
is equivalent to the conjunction
append( [1], [2, 4], _A1 ), List = [ _A1 | _T1 ],
append( [1, 2], [5], _A2 ), _T1 = [ _A2 | _T2 ],
append( [1, 2, 3], [6, 7], _A3 ), _T2 = [ _A3 | _T3 ], _T3 = [].
which will return:
%% List = [ _A1, _A2, _A3 ]
List = [[1, 2, 4], [1, 2, 5], [1, 2, 3, 6, 7]].
Your predicate spaces_in_file takes three arguments, H_V, Line and Spaces, you can do a maplist by fixing H_V and then giving Line and Spaces to maplist as arguments, i.e:
maplist( spaces_in_file(h), Lines, Spaces)

Prolog: How to make a bagof fail in the middle of execution

My problem is that I have a functor with the next structure:
something(Perms, New_perms) :-
bagof(New_perm, aux_something(Perms, New_perm), <New_perms>).
aux_something(Perms, New_perm) :-
member(Perm, Perms),
take_impossible(Perm, New_perms).
take_impossible(Perm_poss, New_perm) :-
Perm_poss = [Var, Perm],
bagof(P, member(P, Perm), subsumes_term(Var, P)), New_perms),
New_perm = [Var, New_perms].
This takes a list of elements of the form:
[[A, 2, C], [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]]
It returns the list of possible permutations:
[[A, 2, C], [[1, 2, 3], [3, 2, 1]]]
But with the list containing an impossible element:
[[A, 4, C], [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]]
The functor should return false.
I need that, when it takes an element of the list which has no possible permutations, the list returns false. A forall instead of the bagof doesn't work because it doesn't change any variable bindings, and a forall before the bagof is too expensive, as I am doing the same operation to all elements of the list twice (it exceeds the limit time in the test). How can I break the execution of a bagof if the condition fails, or is there any other predicate that can do what I am looking for?
(Functor and aux_functor are not the real names of the variables, but this is college work, and so I am trying not to give too much info).
Stupid, question when I found the answer...
A bagof that fails when its objective fails is just a maplist.
Thanks for the help, anyway.
In this case, the correct answer would be:
something(Perms, New_perms) :-
maplist(take_impossible, Perms, New_perms).
take_impossible(Perm_poss, New_perm) :-
Perm_poss = [Var, Perm],
bagof(P, member(P, Perm), subsumes_term(Var, P)), New_perms),
New_perm = [Var, New_perms].

How to create a list without using findall? Prolog

I am generating permutations:
takeout(X,[X|T],T).
takeout(X,[F|R],[F|S]):-
takeout(X,R,S).
perm([],[]).
perm([X|Y],Z):-
perm(Y,W),
takeout(X,Z,W).
I want to know how to create a list of all the permutations without using findall.
Example:
?-perm([1,2,3],List).
List = [[1, 2, 3], [2, 1, 3], [2, 3, 1], [1, 3, 2], [3, 1, 2], [3, 2, 1]]
Group permutations by the element it starts with.
Take an element X and create permutations Ys1 without it in the original list.
Adding this element X as the first element of all these permutations we have the list XP of permutations starting with X.
Appending all the groups will give you all permutations.
cons(X, Xs, [X|Xs]).
perm([], [[]]).
perm(Xs, Ys) :-
dif(Xs, []),
maplist({Xs}/[X, XP]>>(select(X, Xs, Xs1),
perm(Xs1, Ys1),
maplist(cons(X), Ys1, XP)),
Xs, Yss),
append(Yss, Ys).
?- perm([1, 2, 3], X).
X = [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]] ;
false.
?- length(Y, 8), perm(Y, X), length(X, N). %8 factorial
N = 40320
The idea is to generate permutations and test if you already created this permutation. I'm using the inbuild predicate permutation/2.
perm(Ori,Out):-
perm(Ori,[],Out).
perm(Ori,Acc,Ret):-
permutation(Ori,Perm),
\+ member(Perm,Acc),
!,
perm(Ori,[Perm|Acc],Ret).
perm(_,L,L).
?- perm([1,2,3],E).
E = [[3, 2, 1], [3, 1, 2], [2, 3, 1], [2, 1, 3], [1, 3, 2], [1, 2, 3]].
The code is not the fastest one since it checks multiple times for membership.

Removing list within lists based on nth element

I've got a list of lists, for example:
[[1, 2, 3, 2], [1, 3, 4, 3], [1, 4, 5, 4], [2, 3, 5, 6], [1, 5, 6, 5],
[2, 4, 6, 8], [1, 6, 7, 6], [2, 5, 7, 10], [3, 4, 7, 12], [2, 6, 8, 12]]
I'd like to get the last element of and check to see if is the same as the 4th element of any of the other lists. If it is the same then leave the list alone, but if it is unique then remove the list. So in the example above I would be left with:
[[3, 4, 7, 12], [2, 6, 8, 12]]
Essentially I want to be remove all the lists where the last element is unique.
I've written a predicate to get the nth element:
my_membership(X, [X|_]).
my_membership(X, [_|Tail]) :-
my_membership(X, Tail).
where:
my_membership([_,_,_,Fourth],[[3, 4, 7, 12], [2, 6, 8, 12]]).
gives:
Fourth = 12
Fourth = 12
Start by building two basic predicates:
last([X], X).
last([_|T], X) :- last(T, X).
forth([_,_,_,F|_], F).
The first predicate extracts the last element of a list; the second predicate extracts the forth element of a list.
Now you can make a predicate that counts how many tomes an element X appears in forth place in any of the lists of a list of lists. Below, H in [H|T] is a list:
matching_forth([], _, 0).
matching_forth([H|T], X, R) :- forth(H, X), matching_forth(T, X, RR), R is RR + 1.
matching_forth([_|T], X, R) :- matching_forth(T, X, R).
With these predicates in place you can build a predicate for checking your condition. It will have three clauses - for a situation when the list is empty, for when the head list has a matching forth element in another list, and for situations when it doesn't:
my_membership([], [], _).
my_membership([H|T], [H|R], A) :-
last(H, X), matching_forth(A, X, C), C > 1, my_membership(T, R, A).
my_membership([_|T], R, A) :- my_membership(T, R, A).
The first and last clauses are self-explanatory. The middle clause extracts the last element from the head list, counts how many times it matches the forth element in the original list of lists (A stands for "all"), and adds H to the result when there is a match. Adding happens through unification with the head of the result list.
Finally, you need a my_membership/2 predicate to start off the recursive chain that passes along the original list of lists:
my_membership(L, R) :- my_membership(L, R, L).
Demo.
Here's a different twist on a potential solution. It uses an accumulator to collect members that we've seen already and checks along the way. The result saves those that have either been seen or are currently in the tail. It requires the use of the built-in, memberchk/2.
my_membership(L, R) :-
my_membership(L, [], R).
my_membership([], _, []).
my_membership([X|T], Acc, R) :-
X = [_,_,_,D],
( memberchk([_,_,_,D], Acc)
-> R = [X|T1],
Acc1 = Acc
; memberchk([_,_,_,D], T)
-> R = [X|T1],
Acc1 = [X|Acc]
; R = T1,
Acc1 = Acc
),
my_membership(T, Acc1, T1).
| ?- my_membership([[1, 2, 3, 2], [1, 3, 4, 3], [1, 4, 5, 4], [2, 3, 5, 6], [1, 5, 6, 5],
[2, 4, 6, 8], [1, 6, 7, 6], [2, 5, 7, 10], [3, 4, 7, 12], [2, 6, 8, 12]], L).
L = [[2,3,5,6],[1,6,7,6],[3,4,7,12],[2,6,8,12]]
yes

Resources