別の回答で与えられたメモ ( t->tree および e->empty という用語の名前のis/2
代わりに=/2
およびタイプミスを使用) に加えて、アキュムレータを使用して、プロローグ システムが末尾再帰を実行できるようにすることを検討する必要があります。
rbt_count_nodes(T,N):-
rbt_count_nodes(T, 0, N).
rbt_count_nodes(nil,N, N):-!.
rbt_count_nodes(tree(_,L,R),C,N):-
succ(C, C1),
rbt_count_nodes(L,C1,NL),
rbt_count_nodes(R,NL,N).
プロシージャは、2 番目の引数 (アキュムレータ) としてゼロを指定してrbt_count_nodes/2
呼び出すだけです。rbt_count_nodes/3
各再帰ステップで、このアキュムレータは 1 ずつインクリメントされ、再帰を実行します。ベースケースは、アキュムレータを出力と統合するだけで、システムが末尾再帰を実行してスタックスペースを節約できるようにします。
[編集]
コメントによると、真に末尾再帰にする (つまり、すべての再帰呼び出しを末尾呼び出しにする) には、新しい節を rbt_count_nodes/3 に追加 (および別の節を変更) できます。
rbt_count_nodes(nil,N, N):-!.
rbt_count_nodes(tree(Node,tree(LNode, LL, LR),R),C,N):-
rbt_count_nodes(tree(Node, LL, tree(LNode, LR, R)), C, N).
rbt_count_nodes(tree(_,nil,R),C,N):-
succ(C, C1),
rbt_count_nodes(R,C1, N).
このアプローチでは、最初の節は空のノードを処理し、2 番目の節は現在のノードの右側に分岐を「移動」するツリーの左側を処理し、3 番目の節は単にノードの数を計算します。ノード。