Can someone explain to me why this prolog query works the way it does. The definition is:
add(0,Y,Y).
add(succ(X),Y,succ(Z)):- add(X,Y,Z).
Given this:
?- add(succ(succ(succ(0))), succ(succ(0)), R).
Heres the trace of the query:
Call: (6) add(succ(succ(succ(0))), succ(succ(0)), R)
Call: (7) add(succ(succ(0)), succ(succ(0)), _G648)
Call: (8) add(succ(0), succ(succ(0)), _G650)
Call: (9) add(0, succ(succ(0)), _G652)
Exit: (9) add(0, succ(succ(0)), succ(succ(0)))
Exit: (8) add(succ(0), succ(succ(0)), succ(succ(succ(0))))
Exit: (7) add(succ(succ(0)), succ(succ(0)),
succ(succ(succ(succ(0)))))
Exit: (6) add(succ(succ(succ(0))), succ(succ(0)),
succ(succ(succ(succ(succ(0))))))
The part that confused me the most about that tutorial was the fact that in the first argument, the succ is stripped, and it recurses. While recursing though, R gains a succ... HOW?! Also, where does the zero come from at the first exit (9)? I am new to prolog, and I am trying to understand the language for a homework. Any help much appreciated.
Note: for anyone interested, the link to this tutorial is http://www.learnprolognow.org/lpnpage.php?pagetype=html&pageid=lpn-htmlse9
The (hopefully) more legible trace with more annotations is:
As you can see, the four exits are redundant, the final answer is already known at the point of
(9) Exit
; only one exit is enough at that point:That is indeed what happens under the tail call optimization, as the predicate definition is indeed tail recursive and the result under
R
is built in a top-down manner, with the "hole" gradually being filled by instantiation of the logical variables. So thesucc( _ )
is not added after the recursive call, but is established before it. This is also the essence of the tail recursion modulo cons optimization."where does the zero come from at the first exit (9)?"
The call
add(0, succ(succ(0)), _G652)
is unified with the first clause that says that if the first argument ofadd
is zero, then the second and the third are the same. In this particular situatiob variable_G652
becomessucc(succ(0))
."While recursing though, R gains a succ... HOW?!"
This is the result of the application of the second clause. This clause states (roughly) that you first strip
succ
from the first argument, then calladd
recursively, and, finally, add another "layer" ofsucc
to the third argument coming back from this recursive call.The predicate
add
is nothing but a direct implementation of addition in Peano arithmetics: http://en.wikipedia.org/wiki/Peano_axioms#AdditionYou see,
call
andexit
areverbs
, actions that the interpreter takes attempting to solve the query you pose. Then a trace exposes details of actual work done, and lets you view it in historical perspective.When Prolog must choice a rule (a
call
), it uses thename
you give it (so calledfunctor
), and attempts to tounify
each argument in the head of the rule. Then we say usually that Prolog considers also thearity
, i.e. number of arguments, for selection.Unification
attempts to 'make equals' two terms, and the noteworthy results are so calledbindings
of variables. You already know that variables are those names startingUppercase
. Such names identify unspecified values in rules, i.e. areplaceholders
for arguments. To avoid confusion, when Prolog shows the trace, variables are renamed so that we can identify them, because the relevant detail it's theidentities
or bindings established during the proof.Then you see such
_G648
symbols in trace. They stay for the yet to be instanced arguments of the called goal, namelyR
andZ
. R is unique (occurs just in top level call), so this Prolog kindly keep the user friendly name, but Z come from the program, potentially occurs multiple times, then got renamed.To answer this query
Prolog first attempts to match
and fail because succ(succ(succ(0)) can't be made equal to 0. Then attempts
thus must solve these bindings (to the left the caller' terms):
You can see why X becomes
succ(succ(0))
, and we have a new goal to prove, i.e. the rule bodyadd(X,Y,Z)
with the bindings just established:add(succ(succ(0)),succ(succ(0)),_G648)
and so on... until X become
0
and the goal matchesadd(0,Y,Y).
Then Y becomes succ(succ(0)), and noteworthy also give a value to
Z
in the calling rule.HTH