First of all, note that this procedure does not calculate the permutation, but is a kind of sublist: a list with deleted "some" points, where "some" is indicated in general form (one solution is an empty list, and another solution is source list), assuming the input list is well-formed.
If the input list is not formed correctly (it has one element that is not a “point”), then the procedure will fail.
Now let's explain three ar/2 sentences, which are a recursive procedure:
First sentence
ar([], []).
indicates that if the first argument is an empty list, then the second argument is also an input list; that is, for an empty list, the only “sublist” that complies with the rules of procedure is also an empty list. This is also the base case of a recursive procedure.
Second sentence
ar([p(_,_)|L], L1):-ar(L, L2), L1=L2.
can be rewritten without using the variable L2 , because it ultimately combines with L1 :
ar([p(_,_)|L], L1):-ar(L, L1).
This section skips the header of the input list and continues with recursion. When the recursion returns, it will combine the resulting list (second argument to ar/2 ) with the second argument to the sentence chapter.
Third sentence
ar([p(X,Y)|L], L1):-ar(L, L2), L1=[p(X,Y)|L2].
can be rewritten again without using the L2 variable by constructing the resulting list in the sentence header:
ar([p(X,Y)|L], [p(X,Y)|L1]):-ar(L,L1).
In this section, we will examine the list of input data, continue the recursion with the tail, and then combine the second argument of the sentence chapter with the received element and the resulting recursion list. That is, it will save the element (head) of the input list along with the result of the recursion.
Also note that this procedure is not reversible if called with the first argument that is not initialized, and the second argument created will be executed forever.