简体   繁体   中英

List diagonals of a matrix in Prolog

How could I get a list of the diagonals of a matrix of size n*n in Prolog?

Expected behavior:

?- diagonal1([[1,3,2],[4,5,7],[6,8,9]],D1). 
D1=[1,5,9]
?- diagonal2([[1,3,2],[4,5,7],[6,8,9]],D2). 
D2=[2,5,6].

(I'm fine with using built-in functions.)

Here is my solution with foldl/4 :

extract_element(L, L1, [H|L1]):- 
                length(L1, N1), 
                length(L2, N1), 
                append(L2, [H|_], L).

diagonal1(In, Out):- 
                foldl(extract_element, In, [], Res), 
                reverse(Res,Out).

diagonal2(In, Out):- 
                reverse(In, In2),
                foldl(extract_element, In2, [], Out).

Can you see spot the difference in diagonal1/2 and diagnal2/2 implementations?

In diagonal1/2 we returned the result form foldl/4 in Res variable in reverse order that the desired order so we reverse the result to get the correct list. On the other hand in diagonal2/2 you understand the way that foldl/4 works above by reversing the Input list we are able not only to extract the correct elements but now the reversed order returned from foldl/4 is the correct !!!!

Example:

?- diagonal1([[1,3,2],[4,5,7],[6,8,9]],D1).
D1 = [1, 5, 9].

?- diagonal2([[1,3,2],[4,5,7],[6,8,9]],D2).
D2 = [2, 5, 6]. 

Using the maplist/3 and the commonly available reverse/2 :

list_diag1([], []).
list_diag1([[E|_]|Ess], [E|Ds]) :-
    maplist(list_tail, Ess, Ess0),
    list_diag1(Ess0, Ds).

list_tail([_|Es], Es).

list_diag2(Ess,Ds) :-
    maplist(reverse, Ess, Fss),
    list_diag1(Fss, Ds).

Sample queries:

?- list_diag1([[a,b,c],[d,e,f],[g,h,i]], D1).
D1 = [a,e,i].

?- list_diag2([[a,b,c],[d,e,f],[g,h,i]], D2).
D2 = [c,e,g].

Although the original request says that built-ins are fine, I wanted to figure out a solution that didn't involve explicit indexing of a list, or the use of findall , reverse , or append and came up with this:

diag1(Matrix, Diag) :-
    diag1(Matrix, [], Diag).

diag1([], _, []).
diag1([Row|Rows], Fs, [D|Ds]) :-
    front_same_length(Fs, D, Row),
    diag1(Rows, [_|Fs], Ds).

diag2([Row|Rows], Diag) :-
    diag2([Row|Rows], Row, Diag).

diag2([], _, []).
diag2([Row|Rows], [_|Fs], [D|Ds]) :-
    front_same_length(Fs, D, Row),
    diag2(Rows, Fs, Ds).

front_same_length([], D, [D|_]).
front_same_length([_|Xs], D, [_|Rs]) :-
    front_same_length(Xs, D, Rs).

The principle here is to use front_same_length/3 to determine an element at some point inside of a list by using another anonymous list of known length. This yields the following results:

| ?- diag1([[1,2,3],[4,5,6],[7,8,9]], D).

D = [1,5,9]

yes
| ?-  diag2([[1,2,3],[4,5,6],[7,8,9]], D).

D = [3,5,7]

yes
| ?- diag1(Matrix, Diag).

Diag = []
Matrix = [] ? ;

Diag = [A]
Matrix = [[A|_]] ? ;

Diag = [A,B]
Matrix = [[A|_],[_,B|_]] ? ;

Diag = [A,B,C]
Matrix = [[A|_],[_,B|_],[_,_,C|_]] ? ;
...

| ?- diag2(Matrix, Diag).

Diag = [A]
Matrix = [[A]] ? ;

Diag = [A]
Matrix = [[_,A]] ? ;

Diag = [A,B]
Matrix = [[_,A],[B|_]] ? ;

Diag = [A]
Matrix = [[_,_,A]] ? ;

Diag = [A,B]
Matrix = [[_,_,A],[_,B|_]] ? ;

Diag = [A,B,C]
Matrix = [[_,_,A],[_,B|_],[C|_]] ? ;

Diag = [A]
Matrix = [[_,_,_,A]] ? ;
...

It seems well-behaved for matrices whose number of rows is less than or equal to the number of columns based upon the way it's designed. It will simply fail if the number of rows exceeds the number of columns.


Here's an updated solution that works for a general nxm matrix at the expense of leaving a choice point:

| ?- diag1([[a,b],[c,d]], D).

D = [a,d] ? ;

no
| ?- diag2([[a,b],[c,d]], D).

D = [b,c] ? a

no
| ?- diag1([[a,b,c],[d,e,f],[g,h,i]], D).

D = [a,e,i] ? ;

no
| ?- diag2([[a,b,c],[d,e,f],[g,h,i]], D).

D = [c,e,g] ? ;

no
| ?- diag1([[a,b,c],[d,e,f]], D).

D = [a,e] ? a

no
| ?- diag2([[a,b,c],[d,e,f]], D).

D = [c,e] ? ;

no
| ?- diag1([[a,b,c],[d,e,f],[g,h,i],[j,k,l]], D).

D = [a,e,i] ? ;

no
| ?- diag2([[a,b,c],[d,e,f],[g,h,i],[j,k,l]], D).

D = [c,e,g] ? ;

no
| ?-

Running sample queries:

 | ?- diag1([[a,b],[c,d]], D). D = [a,d] ? ; no | ?- diag2([[a,b],[c,d]], D). D = [b,c] ? a no | ?- diag1([[a,b,c],[d,e,f],[g,h,i]], D). D = [a,e,i] ? ; no | ?- diag2([[a,b,c],[d,e,f],[g,h,i]], D). D = [c,e,g] ? ; no | ?- diag1([[a,b,c],[d,e,f]], D). D = [a,e] ? a no | ?- diag2([[a,b,c],[d,e,f]], D). D = [c,e] ? ; no | ?- diag1([[a,b,c],[d,e,f],[g,h,i],[j,k,l]], D). D = [a,e,i] ? ; no | ?- diag2([[a,b,c],[d,e,f],[g,h,i],[j,k,l]], D). D = [c,e,g] ? ; no | ?- 

A basic solution using no built in predicates (except length/2 ) could be:

isSquare([H|T]):-
    length(H,V),
    length([H|T],V).

diagonal(M,D):-
    diagonalA(M,D,1).

diagonalA([],[],_).
diagonalA([H|T],[HD|TD],I):-
    diagonalInner(H,HD,I,1),
    I1 is I+1,
    diagonalA(T,TD,I1).

diagonalInner([H|_],H,I,I):-!.
diagonalInner([_|T],H,I,I1):-
    I2 is I1+1,
    diagonalInner(T,H,I,I2).

antidiagonal([H|T],A):-
    length(H,N),
    antidiagonalA([H|T],A,N).

antidiagonalA([],[],0).
antidiagonalA([H|T],[HA|TA],N):-
    antidiagonalInner(H,HA,N,1),
    N1 is N-1,
    antidiagonalA(T,TA,N1).

antidiagonalInner([H|_],H,I,I):-!.
antidiagonalInner([_|T],H,I,E):-
    E1 is E+1,
    antidiagonalInner(T,H,I,E1).

solve(M,D1,D2):-
    isSquare(M),
    diagonal(M,D1),
    antidiagonal(M,D2).

?- solve([[1,2,3],[4,5,6],[7,8,9]],D1,D2).
D1 = [1, 5, 9],
D2 = [3, 5, 7]

findall/3 and other may be usefull :

diagonales(Matrix, [L1, L2]) :-
    length(Matrix, N),
    findall(B, (between(1,N, I), nth1(I, Matrix, Row), nth1(I, Row, B)), L1),
    findall(B, (between(1,N,I), J is N+1-I, nth1(I, Matrix, Row), nth1(J,Row,B)),L2).
diag(M,D) :- findall(V, (nth1(I,M,X),nth1(I,X,V)), D).
gaid(M,G) :- length(M,L), findall(V, (nth1(I,M,X),J is L-I+1,nth1(J,X,V)), G).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM