Search code examples
matrixprologprolog-findall

Extract Facts as Matrix in Prolog


Suppose we have the following:

edge(a, 1, 10).
edge(b, 2, 20).
edge(c, 3, 30).
edge(d, 4, 40).

I want to extract a matrix representation (M) of these facts, such that

M = [[a,b,c,d],[1,2,3,4],[10,20,30,40]]

Here's a no-brainer solution:

edgeMatrix(M) :-
  findall(A, edge(A, _, _), As),
  findall(B, edge(_, B, _), Bs),
  findall(C, edge(_, _, C), Cs),
  M = [As, Bs, Cs].

There are some problems to this approach, however, viz:

  1. we traverse the database n times, where n is the number of arguments; and
  2. this doesn't generalize very well to an arbitrary n.

So the question is: what is the most idiomatic way to achieve this in Prolog?


Solution

  • What about:

    edgeMatrix(M) :-
        findall([A,B,C],edge(A,B,C),Trans),
        transpose(Trans,M).
    

    Now you can simply import the transpose/2 matrix from clpfd module, or implement one yourself like in this answer (yeah I know that's quite lazy, but what is the point of reinventing the wheel?).

    If I run this in swipl, I get:

    ?- edgeMatrix(M).
    M = [[a, b, c, d], [1, 2, 3, 4], [10, 20, 30, 40]].
    

    which looks exactly like you want.

    You can of course say that there is still some computational overhead to calculate the transpose/2, but the collecting phase is done only once (and if these are not simply facts, but answers from clauses as well) which can be expensive as well, and furthermore I think a module will implement clauses probably very efficiently anyway.