With these it helps In general, you usually provide complex construction like this through methods on the companion object of your (base) class. It provides a nice clean separation of the initialisation code and the post-construction usage code. Also, having immutable data may help design smaller, more focused concerns. For instance you have a box (left, right, top, bottom) that you could separate into its own class (or just a tuple if you like).
I hope this helps . You can do the zeroes by introducing indices that indicate row and column you are at and check for a match:
main(A, O) :-
second(A, A, 0, O).
second(, _, _, ).
second([A|As], B, R, [O|Os]) :- %creates the list of lists.
third(A, B, 0, R, O),
R1 is R + 1,
second(As, B, R1, Os).
third(_, , _, _, ).
third(A, [B|Bs], C, R, [O|Os]) :-
fourth(A, B, C, R, O),
C1 is C + 1,
third(A, Bs, C1, R, Os). %multiplies single digit by list.
fourth(_, _, X, X, 0).
fourth(A, B, C, R, O) :- C \== R, O is A * B.
| ?- main([1,2,2,1], L).
L = [[0,2,2,1],[2,0,4,2],[2,4,0,2],[1,2,2,0]] ? ;
maplist_with_index(Pred, L, M) :-
maplist_with_index_(Pred, 0, L, M).
maplist_with_index_(Pred, I, [H|T], [M|Ms]) :-
Pred =.. [P|Pt],
append([P,I|Pt], [H], NewPred),
Call =.. NewPred,
I1 is I + 1,
maplist_with_index_(Pred, I1, T, Ms).
maplist_with_index_(_, _, , ).
main(A, O) :-
second(A, A, O).
second(M, A, O) :-
maplist_with_index(third(A), M, O).
third(R, A, E, O) :-
maplist_with_index(fourth(R, E), A, O).
fourth(X, X, _, _, 0).
fourth(C, R, A, B, O) :- C \== R, O is A * B.
I hope this helps you . That kind of combination is called the Cartesian product. I'd use itertools.product for this, which can easily cope with more than 2 lists, if you want. Firstly, here's a short demo that shows how to get all of the pairs and how to use tuple assignment to grab the individual elements of the pairs of sublists.
from itertools import product
a = [[1,2],[3,4],[7,10]]
b = [[8,6],[1,9],[2,1],[8,8]]
for (u0, u1), (v0, v1) in product(a, b):
print(u0, u1, v0, v1)