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 n x m matrix at the expense of leaving a choice point:
diag1(Matrix, Diag) :-
diag1(Matrix, [], Diag).
diag1([], _, []).
diag1([Row|Rows], Fs, [D|Ds]) :-
front_same_length(Fs, D, Row),
diag1(Rows, [_|Fs], Ds).
diag1([Row|_], Fs, []) :-
same_length(Row, Fs).
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).
diag2([_|_], [], []).
front_same_length([], D, [D|_]).
front_same_length([_|Xs], D, [_|Rs]) :-
front_same_length(Xs, D, Rs).
same_length([], []).
same_length([_|Xs], [_|Ys]) :-
same_length(Xs, Ys).
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
| ?-