Size Procedure in Prolog Language

2019-07-18 08:35发布

问题:

I am new to Prolog and am not that great when it comes to recursive algorithms as is, thus I am confused with the following two clauses:

size([], 0).
size([H|T], N) :- size(T, N1), N is N1+1.

I am having trouble tracing this problem for:

?- size([a,b,c,d], N).

This will unify with the second clause to form:

size([a,b,c,d], N) :- size([b,c,d], N1), N is N1+1.

But I am confused with the N is N1+1 as these variables are never unified. What values do these variables take?

Any help regarding this question, or a simple trace of this algorithm, would be greatly appreciated.

回答1:

the N is N1+1 as these variables are never unified.

I think you mean that they are never instantiated i.e. they never get a value. Unifying two variables can instantiated them but it's not necessary. For example you could run this in a prolog REPL:

?- N = N1.
N = N1.

while N and N1 have no value (yet), they are unified; if N1 gets instantiated later, N will be instantiated with the same value as well. Another, less trivial, example:

?- [H|T] = L, L = [1|M], writeln(H).
1
H = 1,
T = M,
L = [1|M].

However, it is true that N is never unified with N1+1! is will evaluate N1+1 and that value will be unified with N. This will happen after the inner size([b,c,d],N1) has been evaluated so the N1 variable will have been instantiated.

Essentially, the execution will be like this:

size([a,b,c,d],N).
          size([b,c,d],N1)
               size([c,d],N1)
                   size([d],N1)
                        size([],0)
                        N is 0+1.
                   N is 1+1.
               N is 2+1.
          N is 3+1.

Which is a bit inefficient as we need to keep all the function calls in memory; look into tail-recursion and accumulators to fix this.

Also note that N1 needs to be instantiated only because is is going to evaluate the expression. You could write something like this:

size([],0).
size([_|T], 1+ N1):-size(T,N1).

and if you call it:

?- size([1,2],N).
N = 0+1+1.

Fun, isn't it? You might want to evaluate the last N, e.g. call it as size([1,2],N), Result is N. However, one problem is that we keep a chain of 0+1+1+1+1+...... in memory which can get big very fast. So it's best to use this trick for things that are not going to grow, e.g. expression templates.