Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prolog nth1 anonymous variables

I have a List with Integers and anonymous variables and I try to find the index of a special values. Problem is as soon I'm using nth1/3 to find the indices Prolog assigns values to the anonymous variables and therefore I find way too indices.

Example: List = [1,\_,1], where I want as result X = 1, X = 3 from nth1(X,List,1), but as stated before I get X = 1, X = 2, X = 3.

like image 886
Ben373 Avatar asked May 15 '17 17:05

Ben373


1 Answers

There is a somewhat problematic issue hidden in your requirements: They violate an important declarative property called monotonicity. By this we mean that adding constraints can at most make the solution more specific, never more general.

For example, with the solution you posted, we get:

?- list_el_index([_], 1, N).
false.

Now I add a constraint by imposing an additional requirement on the hitherto free anonymous variable:

?- Var = 1, list_el_index([Var], 1, N).
Var = 1,
N = 0 .

I mean: Come on! We have added a constraint, and as a result get more solutions than before? Such a result is unfortunate and prevents us from reasoning in a logical way about this program.

The program also fails us in other respects. For example, let us ask: Which solutions are there at all?

?- list_el_index(Ls, El, I).
nontermination

Ideally, we would like the program to generate solutions in such cases! This generality is one of the foremost attractions of logic programming, and distinguishes it from more low-level paradigms.


One way to solve such issues is to symbolically distinguish the different kinds of elements that appear in your list.

For example, let us use:

  • u for an unknown value.
  • i(I) for an integer I.

With this new representation, your solution becomes:

list_el_index([i(I)|_], I, 0).
list_el_index([_|Tail], Element, Index) :-
    list_el_index(Tail, Element, Index0),
    Index #= Index0+1.

I have also taken the liberty to replace (is)/2 by (#=)/2, to advertise and stick to more general integer arithmetic that lets us more freely reorder the goals, if necessary. Depending on your Prolog implementation, you may have to import a library to benefit from (#=)/2.

With this representation, your initial case becomes:

?- list_el_index([i(1),u,i(1)], 1, Index).
Index = 0 ;
Index = 2 ;
false.

This works as desired!

Importantly, we can use the predicate also more generally, namely to generate possible answers:

?- list_el_index(Ls, El, I).
Ls = [i(El)|_2994],
I = 0 ;
Ls = [_2992, i(El)|_3000],
I = 1 ;
Ls = [_2992, _2998, i(El)|_3006],
I = 2 ;
Ls = [_2992, _2998, _3004, i(El)|_3012],
I = 3 .

Due to the program's monotonicity, we can fairly enumerate solutions by iterative deepening:

?- length(Ls, _), list_el_index(Ls, El, I).
Ls = [i(El)],
I = 0 ;
Ls = [i(El), _4812],
I = 0 ;
Ls = [_4806, i(El)],
I = 1 ;
Ls = [i(El), _4812, _4818],
I = 0 ;
etc.

This has become possible by using a representation that lets us distinguish the cases by pattern matching. Consider using this approach to make your programs usable in all directions, and to make logical reasoning applicable. It is quite easy to apply by using the appropriate wrapper or constant, and greatly increases the generality of your programs.

like image 103
mat Avatar answered Sep 28 '22 05:09

mat