Related
I am trying to make a List of Lists of Lists without values. If N_meses = 4 I want List =[[[A,B,C,D]]].
I get what I want ( List = [[[]]] ) but every lists have the same values as you can see in the print I attached. How can I change this code so every lists have a different "value"?
I am doing this
generate_table(Num_investigadores, Num_actividades, N_Meses, Tabela) :-
length(Row, Num_actividades),
length(X,N_Meses),
maplist(=(X), Row),
length(Tabela, Num_investigadores),
maplist(=(Row), Tabela).
The culprit is in essence the:
%% ...
maplist(=(X), Row),
%% ...
Here you basically defined a list X, and then you set with maplist/2 that all elements in Row are unified with that X. In the unification process. This thus means that all the elements of Row will in essence point to the same list.
Nevertheless, I think it would definitely help if you make the predicate less ambitious: implement helper predicates and let each predicate do a small number of things.
We can for example first design a predicate lengthlist/2 that is the "swapped" version of length/2, and thus has as first parameter the length and as second parameter the list, like:
lengthlist(N, L) :-
length(L, N).
Now we can construct a predicate that generates a 2d rectangular list, for example:
matrix(M, N, R) :-
lengthlist(M, R),
maplist(lengthlist(N), R).
here we thus first use lengthlist to construct a list with N elements, and then we use maplist/2 to call lengthlist(N, ...) on every element, such that every element is unified with a list of N elements. We thus construct a 2d list with M elements where every elements is a list of N elements.
Then finally we can construct a 3d tensor:
tensor3(L, M, N, T) :-
lengthlist(L, T),
maplist(matrix(M, N), T).
Here we thus construct an L×M×N tensor.
We can in fact generalize the above to construct a arbitrary deep cascade of lists that is "rectangular" (in the sense that for each dimension, the lists have the same number of elements), but I leave this as an exercise.
I have this program to generate all the permutations of a list. The thing is, I need to generate only the permutations in which the consecutive terms have the absolute difference less or equal than 3. Something like:
[2,7,5] => [2,5,7] and [7,5,2]. [2 7 5] would be wrong since 2-7 = -5 and |-5| > 3
The permutation program:
perm([X|Y],Z):-
perm(Y,W),
takeout(X,Z,W).
perm([],[]).
takeout(X,[X|R],R).
takeout(X,[F|R],[F|S]):-
takeout(X,R,S).
permutfin(X,R):-
findall(P,perm(X,P),R).
I know I'm supposed to add the condition somewhere in the perm function but I can't figure out exactly what or where to write.
A more intuitive way to write a permutation is:
takeout([X|T],X,T).
takeout([H|L],X,[H|T]) :-
takeout(L,X,T).
Where the first element is the original list, the second the element picked, and the third the list without that element.
In that case the permutation predicate is defined as:
perm([],[]).
perm(L,[E|T]) :-
takeout(L,E,R),
perm(R,T).
this also allows tail-recursion which can imply an important optimization in most Prolog systems.
Now in order to generate only permutations with a consecutive difference of at most three, you can do two things:
The naive way is generate and test: here you let Prolog generate a permutation, but you only accept it if a certain condition is met. For instance:
dif3([_]).
dif3([A,B|T]) :-
D is abs(A-B),
D =< 3,
dif3([B|T]).
and then define:
perm3(L,R) :-
perm(L,R),
dif3(R).
This approach is not very efficient: it can be the case that for an exponential amount of permutations, only a few are valid, and this would imply a large computational effort. If for instance the list of elements is [2,5,7,9] it will generate all permutations starting with [2,9,...] while a more intelligent approach could already see that will never generate a valid solution anyway.
the other more intelligent approach is interleaved generate and test. Here you select only numbers with takeout3/4 that are valid candidates. You can define a predicate takeout3(L,P,X,T). where L is the original list, P the previous number, X the selected number and T the resulting list:
takeout3([X|T],P,X,T) :-
D is abs(X-P),
D =< 3.
takeout3([H|L],N,X,[H|T]) :-
takeout3(L,N,X,T).
Now we can generate a permutation as follows:
perm3([],[]).
perm3(L,[E|T]) :-
takeout(L,E,R),
perm3(R,E,T).
perm3([],_,[]).
perm3(L,O,[E|T]) :-
takeout3(L,O,E,R),
perm3(R,E,T).
Mind we use two versions of perm3: perm3/2 and perm3/3, the first is used to generate the first element (using the old takeout/3), and perm3/3 is used to generate the remainder of the permutation using takeout3/4.
The full source code of this approach is:
takeout([X|T],X,T).
takeout([H|L],X,[H|T]) :-
takeout(L,X,T).
takeout3([X|T],P,X,T) :-
D is abs(X-P),
D =< 3.
takeout3([H|L],N,X,[H|T]) :-
takeout3(L,N,X,T).
perm3([],[]).
perm3(L,[E|T]) :-
takeout(L,E,R),
perm3(R,E,T).
perm3([],_,[]).
perm3(L,O,[E|T]) :-
takeout3(L,O,E,R),
perm3(R,E,T).
Running it with swipl gives:
?- perm3([2,7,5],L).
L = [2, 5, 7] ;
L = [7, 5, 2] ;
false.
The expected behavior.
Here is another solution. I added the condition in takeout to make sure the adjacent items are within 3 of each other:
perm([X|Y],Z):-
perm(Y,W),
takeout(X,Z,W).
perm([],[]).
check(_,[]).
check(X,[H|_]) :-
D is X - H,
D < 4,
D > -4.
takeout(X,[X|R],R) :-
check(X,R).
takeout(X,[F|R],[F|S]):-
takeout(X,R,S),
check(F,R).
I'm doing some experiments with Prolog and having difficulties with the following rule:
row(Row, Matrix, [R1,R2,R3,R4]) :-
cell(1, Row, Matrix, R1),
cell(2, Row, Matrix, R2),
cell(3, Row, Matrix, R3),
cell(4, Row, Matrix, R4).
This rule extracts one row from a matrix, given its row number. For example,
row(2, [1,2,3,4,5,6,7,8], X)
X = [5,6,7,8]
What nags me is that there is lots of repetition in that code. After finishing with 4x4 matrices, I will have to deal with 9x9 ones. And the code can get very non DRY.
Is there a way to extract that repetition out?
Thanks.
Edit: The complete code giving me trouble is here: https://github.com/kikito/7-languages-in-7-weeks/blob/master/3-prolog/day-3/sudoku-refactor.pl
After writing my first answer, I realized that you can also simplify your program using findall
row(Row, Matrix, L) :- findall(X,cell(_,Row,Matrix,X),L).
I would think about changing the representation to a list of lists rather than a flat list, then selecting a row becomes very easy. You can just use the built-in nth1/3:
:- use_module(library(lists)). % I use Sicstus Prolog
row(N,M,X) :- nth1(N,M,X).
cell(R,C,M,X) :- nth1(R,M,Y), nth1(C,Y,X).
column(N,M,X) :- findall(Y,(nth1(_,M,Z), nth1(N,Z,Y)),X).
m([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]]).
example(Row,Cell,Column) :- m(M), row(2,M,Row), cell(2,3,M,Cell), column(2,M,Column).
%| ?- example(A,B,C).
%A = [5,6,7,8],
%B = 7,
%C = [2,6,10,14] ? ;
%no
I am trying to learn a little bit about swi-prolog (beyond the basic, useless programs).
Can anyone explain (perhaps in pseudocode) what this sudoku solver and the related functions are doing? If you need more reference it is found in the CLP(FD) package of swi-prolog.
Thanks!
:- use_module(library(clpfd)).
sudoku(Rows) :-
length(Rows, 9), maplist(length_(9), Rows),
append(Rows, Vs), Vs ins 1..9,
maplist(all_distinct, Rows),
transpose(Rows, Columns), maplist(all_distinct, Columns),
Rows = [A,B,C,D,E,F,G,H,I],
blocks(A, B, C), blocks(D, E, F), blocks(G, H, I).
length_(L, Ls) :- length(Ls, L).
blocks([], [], []).
blocks([A,B,C|Bs1], [D,E,F|Bs2], [G,H,I|Bs3]) :-
all_distinct([A,B,C,D,E,F,G,H,I]),
blocks(Bs1, Bs2, Bs3).
problem(1, [[_,_,_,_,_,_,_,_,_],
[_,_,_,_,_,3,_,8,5],
[_,_,1,_,2,_,_,_,_],
[_,_,_,5,_,7,_,_,_],
[_,_,4,_,_,_,1,_,_],
[_,9,_,_,_,_,_,_,_],
[5,_,_,_,_,_,_,7,3],
[_,_,2,_,1,_,_,_,_],
[_,_,_,_,4,_,_,_,9]]).
Prolog is a different way of thinking about programs: you have to think logically.
First of all A :- B, C, D means A is true (succeds) if B AND C AND D are true.
The snippet of code you posted checks for the correctness of a Sudoku puzzle, there are three condition:
elements are all different by rows
elements are all different by columns
elements are all different by 3x3 blocks
How does it work?
sudoku(Rows) is true if:
length(Rows, 9) -> there are 9 elements in rows
maplist(_length(9), Rows) -> maplist checks the predicate (first parameter) on every element of the list (second parameter). This means that every row must be of length 9.
maplist(all_distinct, Rows) -> same as before, but we check if every row has distinct (not equal pairwise) elements.
transpose(Rows, Columns), maplist(all_distinct, Columns) -> we transpose the rows into columns to check if they are all distinct also by selecting them in the vertical way
Rows = [A,B,C,D,E,F,G,H,I] -> splits rows list and place every one in a different variable A, B, C, D ... so A will be first row, B second one and so on
blocks(A, B, C), blocks(D, E, F), blocks(G, H, I) -> this predicate must be true for the triplets of rows.
Let's talk about the blocks part, that is quite funny to understand. We want to check that every 3x3 block contains distinct values. How can we do that?
Suppose to have 3 rows, the condition must be true for first three elements of every row (first 3x3 block), for elements 4th to 6th (second block) and 7th-9th (third block).
So we can think recursively: blocks([],[],[]) is trivially true, we've got empty lists.
The case blocks([A,B,C|Bs1],[D,E,F|Bs2],[G,H,I|Bs3]) is chosen when you call blocks predicate with parameters that are list with AT LEAST 3 elements. So we can check if A,B,C,D,E,F,G,H,I are all distinct, then we call blocks recursively using as parameters the remainder lists (without the first three elements). This is what Prolog is about!
So blocks will be called first with three rows of 9 elements, it will check that first 3 of every row are distinct and call itself with 3 lists of 6 elements, check it again and call itself with 3 lists of 3 elements, check it again and call itself with three empty lists (the trival case that always succeds).
sudoku/1 basically describes the constraints a Sudoku solution must satisfy, where the board is represented as a list of nine lists of length nine. problem/2 assigns a partially instantiated board to a problem number. To use it you should do
?- problem(1, Board), sudoku(Board).
You should read up on the predicates used in the documentation.
about "append(Rows, Vs), Vs ins 1..9"
http://www.swi-prolog.org/pldoc/man?predicate=append%2F2
It means that all elements of the list of lists must be in the domain 1..9.
I'm trying to make a function that has a list of lists, it multiplies the sum of the inner list with the outer list.
So far i can sum a list, i've made a function sumlist([1..n],X) that will return X = (result). But i cannot get another function to usefully work with that function, i've tried both is and = to no avail.
Is this what you mean?
prodsumlist([], 1).
prodsumlist([Head | Tail], Result) :-
sumlist(Head, Sum_Of_Head),
prodsumlist(Tail, ProdSum_Of_Tail),
Result is Sum_Of_Head * ProdSum_Of_Tail.
where sumlist/2 is a SWI-Prolog built-in.
Usage example:
?- prodsumlist([[1, 2], [3], [-4]], Result).
Result = -36.
The part "it multiplies the sum of the inner list with the outer list" isn't really clear, but I believe you mean that, given a list [L1,...,Ln] of lists of numbers, you want to calculate S1*..*Sn where Si is the sum of the elements in Li (for each i).
I assume the existence of plus and mult with their obvious meaning (e.g. plus(N,M,R) holds precisely when R is equal to N+M). First we need predicate sum such that sum(L,S) holds if, and only if, S is the sum of the elements of L. If L is empty, S obviously must be 0:
sum([],0).
If L is not empty but of the form [N|L2], then we have that S must be N plus the sum S2 of the elements in L2. In other words, we must have both sum(L2,S2) (to get S2 to be the sum of the elements of L2) and plus(N,S2,S). That is:
sum([N|L2],S) :- sum(L2,S2), plus(N,S2,S).
In the same way you can figure out the predicate p you are looking for. We want that p(L,R) holds if, and only if, R is the product of S1 through Sn where L=[L1,...,Ln] and sum(Li,Si) for all i. If L is empty, R must be 1:
p([],1).
If L is not empty but of the form [LL|L2], then we have that R must be the product of 'S', the sum of the elements of LL, and 'P', the product of the sums of the lists in L2. For S we have already have sum(LL,S), so this gives us the following.
p([LL|L2],R) :- sum(LL,S), p(L2,P), mult(S,P,R).
One thing I would like to add is that it is probably not such a good idea to see these predicates as functions you might be used to from imperative or functional programming. It is not the case that sumlist([1,..,n],X) returns X = (result); (result) is a value for X such that sumlist([1,...,n],X) is true. This requires a somewhat different mindset. Instead of thinking "How can I calculate X such that p(X) holds?" you must think "When does P(X) hold?" and use the answer ("Well, if q(X) or r(X)!") to make the clauses (p(X) :- q(X) and p(X) :- r(X)).
Here is a rewrite of Kaarel's answer (that's the intention anyway!) but tail-recursive.
prodsumlist(List, Result) :-
xprodsumlist(List,1,Result).
xprodsumlist([],R,R).
xprodsumlist([Head|Rest],Sofar,Result) :-
sumlist(Head, Sum_Of_Head),
NewSofar is Sofar * Sum_Of_Head,
xprodsumlist(Rest, NewSofar, Result).