为了扩展我的评论,也许这可能会有所帮助:
?- findall(X, member(X, [1, 2, 3]), Xs).
Xs = [1, 2, 3].
如果您仔细观察,您会发现 Prolog(在本例中为 SWI)没有打印X
. 这意味着X
查询成功时不绑定。的确:
?- findall(X, member(X, [1, 2, 3]), Xs), var(X).
Xs = [1, 2, 3].
这并不意味着在查询执行X
时永远不会绑定:
?- findall(X, ( member(X, [1, 2, 3]), writeln(X) ), Xs), var(X).
1
2
3
Xs = [1, 2, 3].
但是在生成所有解决方案之后,X
它是未绑定的并且可以绑定到其他一些值——例如解决方案列表。这将适用于任何符合 Prolog 的标准,正如标准明确指出的那样,它findall
只会在创建解决方案列表后尝试统一其第三个参数。它甚至包含一个在模板和实例化列表之间共享的示例:
findall(X, (X=1;X=2), [X, Y]).
Succeeds, unifying X with 1, and Y with 2.
那么这种绑定和解除绑定是如何工作的呢?使用失败驱动的循环,正如 rajashekar 对 SWI-Prolog 实现的回答所引用的那样。通常,后续谓词绑定一些变量。当稍后某个时间出现故障时(或者,等效地,用户;在顶层提示时按下),就会发生回溯:它取消绑定变量以允许它们采用新值,然后重试某个目标。
内部findall
发生的事情与您编写以下内容时发生的事情相同:
?- ( member(X, [1, 2, 3]), writeln(X), false ; true ), var(X).
1
2
3
true.
因此,虽然findall
非常不纯,但它并没有完全不纯,以至于完全不像 Prolog。其实我们可以自己写:
:- dynamic my_findall_bag/1.
my_findall(Template, Goal, Instances) :-
% initialization
retractall(my_findall_bag(_)),
asserta(my_findall_bag([])),
% collect solutions
( call(Goal),
copy_term(Template, NewSolution),
retract(my_findall_bag(PreviousSolutions)),
asserta(my_findall_bag([NewSolution | PreviousSolutions])),
% failure-driven loop: after saving the solution, force Goal to
% generate a new one
false
; true ),
% cleanup and finish; the saved solutions are in reversed order (newest
% first), so reverse them
retract(my_findall_bag(AllSavedSolutions)),
reverse(AllSavedSolutions, Instances).
这表现如预期:
?- my_findall(X, member(X, [1, 2, 3]), Xs).
Xs = [1, 2, 3].
甚至:
?- my_findall(X, member(X, [1, 2, 3]), X).
X = [1, 2, 3].
一个小问题是Goal
应该检查的实例化。这样做的一个主要问题是所有my_findall
调用共享同一个包,因此my_findall
从内部调用my_findall
(或并行调用)会让你不开心。这可以使用某种gensym
机制来解决,让每次my_findall
运行都将其唯一的密钥输入数据库。
至于跟踪输出,想要在一行上表达“您的目标通过某某绑定成功”是一个不幸的结果。在成功点上,成功是真的,findall(X, ..., X)
是真的,X = [1, 2, 3]
因此目标的成功实例是 是findall([1, 2, 3], ..., [1, 2, 3])
真的。
考虑:
forty_two(FortyTwo) :-
var(FortyTwo),
FortyTwo = 42.
my_call(Goal) :-
format('about to call ~w~n', [Goal]),
call(Goal),
format('success: ~w~n', [Goal]).
例如:
?- my_call(forty_two(X)).
about to call forty_two(_2320)
success: forty_two(42)
X = 42.
forty_two(42)
的后续实例也是如此forty_two(X)
。即使forty_two(42)
没有成功:
?- forty_two(42).
false.
在带有prints的环境中打印该术语是合乎逻辑的。我认为问题在于,这种逻辑行为在这里发生的所有非逻辑事物中显得很奇怪。forty_two(X)
X = 42
forty_two(42)