まず、\+
目標の否定です。それに続くゴールが失敗したとき、それは成功します。例えば:
?- member(3, [1,2,3]). # 3 is a member of the List
true.
?- member(4, [1,2,3]). # 4 is not a member
false.
?- \+member(4, [1,2,3]). # succeeds, because 'member' fails
true.
?- \+member(3, [1,2,3]).
false.
?- atom_chars('hi', C).
C = [h, i].
?- atom_chars('hi', [h, i]).
true.
?- atom_chars('hello', [h, i]).
false.
?- \+atom_chars('hello', [h, i]).
true.
第二に、アキュムレータは、末尾再帰の最適化を利用するために、再帰を介して状態をスレッド化する方法です。
階乗を計算する次の2つの同等の方法を検討してください。
?- [user].
|: fact_simple(0, 1).
|: fact_simple(N, F) :-
N1 is N-1,
fact_simple(N1, F1),
F is N*F1.
|: % user://2 compiled 0.00 sec, 440 bytes
true.
?- fact_simple(6, F).
F = 720 .
[user].
|: fact_acc(N, F) :- fact_acc(N, 1, F).
|: fact_acc(0, Acc, Acc).
|: fact_acc(N, Acc0, F) :-
N1 is N-1,
Acc is Acc0 * N,
fact_acc(N1, Acc, F).
|: % user://4 compiled 0.00 sec, 1,912 bytes
true.
?- fact_acc(6, F).
F = 720 .
最初のバージョンは、再帰で自分自身を呼び出すだけで、サブコールが完了するのを待ちます。N
その場合にのみ、-valueにサブコールの結果を乗算します。
2番目のバージョンでは、代わりにアキュムレータを使用しています(Acc
)。1
これはアキュムレータではありませんが、初期値であることに注意してください。その後、述語を呼び出すたびに、その値にアキュムレータが乗算さN
れ、再帰がベースケースに達すると、アキュムレータの値はすでに最終値になっています。
問題は実際にはそうではありません'アキュムレータは何ができるのでしょうか?(0または空のリストなど)。これは、「必要に応じて」値を累積する方法にすぎず、呼び出し元の述語に戻ることはありません。このように、Prologシステムは増え続けるコールスタックを構築する必要はありません。
ただし、この例では、乗算の順序が自然に逆になっていることに注意してください。乗算は重要ではありませんが、他の値(リストなど)の場合は、それを処理する必要があります。
fact_simple
として乗算を行いましたが、1 * 2 * 3 * 4 * 5 * 6
としてfact_acc
持っていました1 * 6 * 5 * 4 * 3 * 2
。それが不明な場合は、両方のトレースを実行してください。