Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prolog - Return the n-th row of a matrix

I am trying to write the predicate rowN/3 which returns the n-th element (in this case row) of a matrix. Example:

?- rowN([[1,2],[3,4],[5,6]], 2, R). 
R = [3,4];
No

I am struggling with the counter. I have tried unsuccessfully to find some pretty similar examples. So far I've managed to write this:

Code:

rowN(L,[],[]).
rowN([],X,[]).
rowN([],[],[].
rowN([H|T],X,R) :-
    A==X,
    A is A + 1,
    rowI(T,A,H).
like image 877
Saul Garcia Avatar asked Dec 08 '15 22:12

Saul Garcia


People also ask

How do you find the nth element in Prolog?

To find the nth element of a list (where n is relative to zero), something like this ought to suffice: find_nth_element_of_list( 0 , X , [X|_] ) .

How do you get the second element in a list in Prolog?

You can move unification into the head of the clause and simply write: second([_, Second| _], Second). The notation for lists is to write the initial elements separated by commas and then a vertical bar to separate the list tail, i.e. the list containing the rest of the elements.

How do you get the head and tail of a list in Prolog?

In Prolog list elements are enclosed by brackets and separated by commas. Another way to represent a list is to use the head/tail notation [H|T]. Here the head of the list, H, is separated from the tail of the list, T, by a vertical bar.

How do you print a matrix in Prolog?

The simplest way to do it is to use write and nl in a recursive rule, like this: print_matrix([]). print_matrix([H|T]) :- write(H), nl, print_matrix(T). This is the "classic" Prolog solution for list processing, with a fact handling the empty list, and a rule that processes header elements one-by-one.


1 Answers

This line does not make much sense:

rowN(L,[],[]).

because the second argument is an integer (if I understand correctly), and you use a list. This is the case with nearly all your arguments. Furthermore you use RowI in your recursive call?

Solution

A solution is to first specify that the first row (I = 1), is equal to the head of the matrix:

rowN([H|_],1,H).

next you need to find an iterative way to enumerate through your matrix. So the header is definitely something of the form:

rowN([H|T],I,X) :-
#   ...

Now we will assume that I is not equal to 1 (we will discuss this topic later). In that case we need to traverse the matrix further, so we will take the tail and set the counter I one back. This can be done using:

rowN([_|T],I,X) :-
    I1 is I-1,
    rowN(T,I1,X).

So our predicate reads:

rowN([H|_],1,H).
rowN([_|T],I,X) :-
    I1 is I-1,
    rowN(T,I1,X).

Now if you use this predicate, it will give the correct result:

?- rowN([[1,2],[3,4],[5,6]], 2, R).
R = [3, 4] ;
false.

The question is why does the predicate does not generate other results: after showing the first result, for rowN([[1,2],[3,4],[5,6]], 2, R) :- rowN([[3,4],[5,6]],1,[3,4])., it could try to find alternatives. It does so by using the second clause, but then it will eventually run out of rows and call for the rowN([],_,_) predicate, since not of the clauses match, it will fail.

This solution is not perfect: it does not work in all directions correctly, which is in general hard in Prolog. That's why good Prolog programmers have written libraries.

Using swi-prolog's builtin nth1/3

Instead of reinventing the wheel, you can make use of the nth1/3 predicate in swi-prolog. Although the arguments are swapped - you need to call it like nth1(2,[[1,2],[3,4],[5,6]],R). - it has the advantage that it works in more directions that what most people can come up in an fast solution, it is with near certainty bugfree (because it has been tested billions of times by all Prolog programs that use the predicate) and some of these builtins are implemented in C++ making them sometimes faster. For instance:

?- nth1(2, [[1,2],[3,4],[5,6]], R).
R = [3, 4].

?- nth1(I, [[1,2],[3,4],[5,6]], [5,6]).
I = 3.

?- nth1(I, [[1,2],[3,4],[5,6]], R).
I = 1,
R = [1, 2] ;
I = 2,
R = [3, 4] ;
I = 3,
R = [5, 6].

?- nth1(I,M,[2,3]).
I = 1,
M = [[2, 3]|_G23] ;
I = 2,
M = [_G22, [2, 3]|_G26] ;
I = 3,
M = [_G22, _G25, [2, 3]|_G29] ;
I = 4,
M = [_G22, _G25, _G28, [2, 3]|_G32] .

You can thus ask what the second row is, ask where the row [5,6] is located, make the query more generic by answering with tuples of the index I and the row R and generate a matrix with a row [2,3] somewhere.

like image 65
Willem Van Onsem Avatar answered Sep 26 '22 10:09

Willem Van Onsem