关于Prolog自然数求和递归中Z值变化的技术问询
先把你的代码和补全后的完整栈追踪信息放出来,方便咱们对照着拆解:
你的求和代码
sum1(1,1). sum1(N, Sum) :- Next is N-1, sum1(Next, Z), Sum is Z + N.
完整栈追踪信息
?- sum1(3,_).
Call: (8) sum1(3, _2668) ? creep
Call: (9) _2860 is 3+ -1 ? creep
Exit: (9) 2 is 3+ -1 ? creep
Call: (9) sum1(2, _2862) ? creep
Call: (10) _2866 is 2+ -1 ? creep
Exit: (10) 1 is 2+ -1 ? creep
Call: (10) sum1(1, _2868) ? creep
Exit: (10) sum1(1, 1) ? creep
Call: (10) _2862 is 1 + 2 ? creep
Exit: (10) 3 is 1 + 2 ? creep
Exit: (9) sum1(2, 3) ? creep
Call: (9) _2668 is 3 + 3 ? creep
Exit: (9) 6 is 3 + 3 ? creep
Exit: (8) sum1(3, 6) ? creep
其实核心要搞懂Prolog递归先“递”下去、再“归”回来的执行逻辑,Z就是这个“归”过程里传递结果的“接力棒”,咱们一步步对应栈追踪理清楚:
初始调用:sum1(3, _)
程序先算出Next = 3-1 = 2,接着调用sum1(2, Z)。这里的Z只是个“空白占位符”,得等sum1(2, Z)执行完,才能拿到它的结果。第二层递归:sum1(2, Z)
同样先算Next = 2-1 =1,再调用sum1(1, Z)。注意!这里的Z和上一层的Z不是同一个变量——Prolog里每一层递归的变量都是独立的,栈追踪里用_2862、_2868这种内存标识区分,咱们代码里只是用了同一个名字Z而已。这时候这个Z还是空白的,等着下一层返回结果。触达基础规则:sum1(1, Z)
这时候匹配到第一条规则sum1(1,1),这个Z直接被绑定为1。这层递归跑完,就把这个1“传递”回上一层的Z(也就是栈里的_2862)。回到第二层:计算sum1(2, Sum)
现在上一层的Z已经拿到了1,接着执行Sum = Z + 2 = 1+2=3,这层的Sum就变成3,再把这个3传递回最上层的Z(栈里的_2668对应的占位符)。回到最外层:得到最终结果
最上层的Z拿到了3,执行Sum = Z +3 =3+3=6,这就是最终的求和结果啦!
简单总结:
- Z是每一层递归用来“接住下一层求和结果”的变量,递归往下走时它是空白的,等触达最底层的基础情况后,才会从下往上一步步被赋值,把结果传递回来。
- 栈追踪里那些带下划线的编号,就是每一层Z的真实身份——它们是不同的变量,只是咱们代码里用了同一个名字Z,别被这个名字迷惑哦~
内容的提问来源于stack exchange,提问作者Markandeya

