Will Ness 的回答应该让您清楚地了解该谓词定义。但是你可以从中学到更多东西。让我们跟踪一个简单的调用。这里我使用的是 GNU Prolog,但大多数 Prolog 系统都提供相同的跟踪功能:
| ?- trace.
The debugger will first creep -- showing everything (trace)
yes
{trace}
| ?- total_item([1,2,3], N).
1 1 Call: total_item([1,2,3],_285) ?
2 2 Call: total_item([2,3],_354) ?
3 3 Call: total_item([3],_378) ?
4 4 Call: total_item([],_402) ?
4 4 Exit: total_item([],0) ?
5 4 Call: _430 is 0+1 ?
5 4 Exit: 1 is 0+1 ?
3 3 Exit: total_item([3],1) ?
6 3 Call: _459 is 1+1 ?
6 3 Exit: 2 is 1+1 ?
2 2 Exit: total_item([2,3],2) ?
7 2 Call: _285 is 2+1 ?
7 2 Exit: 3 is 2+1 ?
1 1 Exit: total_item([1,2,3],3) ?
N = 3
(1 ms) yes
您可以在前四行中看到谓词导致列表遍历直到结束,只有在那时我们才计算 待处理N is C + 1 目标。列表中的每个元素都会导致一个未决的算术评估目标。这些未决目标必须存储在堆栈中,直到对total_item /2 谓词的递归调用终止。因此,计算大小为 N 的列表的长度需要与 N 成比例的空间(用于堆栈)。这个谓词定义不是尾递归。
但我们可以重写谓词,使其尾递归通过使用累加器来提高其空间复杂度。累加器是一个存储中间结果的参数,在这种情况下,是列表中元素数量的计数:
total_item(List, Total) :-
total_item(List, 0, Total).
total_item([], Total, Total).
total_item([_| Rest], Total0, Total) :-
Total1 is Total0 + 1,
total_item(Rest, Total1, Total).
让我们追踪同一个目标:
| ?- total_item([1,2,3], N).
1 1 Call: total_item([1,2,3],_285) ?
2 2 Call: total_item([1,2,3],0,_285) ?
3 3 Call: _382 is 0+1 ?
3 3 Exit: 1 is 0+1 ?
4 3 Call: total_item([2,3],1,_285) ?
5 4 Call: _435 is 1+1 ?
5 4 Exit: 2 is 1+1 ?
6 4 Call: total_item([3],2,_285) ?
7 5 Call: _488 is 2+1 ?
7 5 Exit: 3 is 2+1 ?
8 5 Call: total_item([],3,_285) ?
8 5 Exit: total_item([],3,3) ?
6 4 Exit: total_item([3],2,3) ?
4 3 Exit: total_item([2,3],1,3) ?
2 2 Exit: total_item([1,2,3],0,3) ?
1 1 Exit: total_item([1,2,3],3) ?
N = 3
(1 ms) yes
请注意,当我们遍历列表时,辅助total_item/3 谓词中的第二个参数累加器是如何递增的。有了这个定义,就没有未决的目标,这意味着空间复杂度是 O(1) 而不是 O(N)。好多了。