PROLOG,是否可以将谓词的所有结果收集到列表中,而不使用内置谓词,例如 bagof 或 findall

PROLOG, Is it possible to collect all result from a predicate to a list, without using built in predicates, such as bagof or findall

例如,如果我有一个 Prolog 谓词,例如 a(A, B).

是否可以收集,给定 A 的值,是否可以将谓词 a 之后的所有 B 值收集到列表中,而不使用内置谓词,例如 bagof/3findall/3.

你有两个明显的选择(对我来说很明显;似乎还有更多)。一种是确实使用数据库来保存状态。这至少有一个陷阱:根据您决定为临时状态使用的名称,您可能会破坏程序保留的其他一些状态。这是所有语言都遇到的老“全局状态”/“全局变量”问题。

另一种选择是使用“局部变量”和对其进行非回溯赋值以保持临时状态。这很可能取决于实现。对于初学者,您可以查看 nb_setarg/3 for SWI-Prolog。

但是,鉴于您有 findall、bagof、setof,这两种解决方案都很愚蠢。你必须激发对其他东西的需求来取代它们。仅仅说“这可能吗”是不够的,因为它是可能的,但完全没有必要,除非你知道你没有告诉我们的其他事情。

这是一个使用其他内置函数的愚蠢 setof 的草图,虽然不是 assert,也不完全是 @false 在评论中列出的那些。

我们将使用列表累加器来收集解决方案:

stupid_setof(Template, Goal, Set) :-
    stupid_setof(Template, Goal, [], Set).

有两种情况需要考虑:要么 Goal 可以枚举我们目前还没有见过的解决方案,要么它可以枚举的唯一解决方案已经在我们的累加器中。

首先,没有我们没有见过的解决方案的情况。在这种情况下我们完成了。

stupid_setof(Template, Goal, SolutionsSeen, Set) :-
    \+ (  call(Goal),
          \+ member(Template, SolutionsSeen) ),
    !,    
    sort(SolutionsSeen, Set).

现在是愚蠢的部分。考虑:

foo(a).
foo(b).
foo(c).

?- SolutionsSeen = [], foo(X), \+ member(X, SolutionsSeen), !.
SolutionsSeen = [],
X = a.

?- SolutionsSeen = [a], foo(X), \+ member(X, SolutionsSeen), !.
SolutionsSeen = [a],
X = b.

?- SolutionsSeen = [a, b], foo(X), \+ member(X, SolutionsSeen), !.
SolutionsSeen = [a, b],
X = c.

?- SolutionsSeen = [a, b, c], foo(X), \+ member(X, SolutionsSeen), !.
false.

所以给定我们以前见过的解决方案列表,我们可以强制 Goal 回溯,直到它给我们一个我们以前没有见过的解决方案。请注意,这些查询是独立的:在每个查询中,我们都有一个从 a.

开始枚举的 foo(X) 目标的全新副本

我们可以通过在调用它之前复制原始目标,以编程方式做同样的事情,迫使它从 Goal 的新实例开始新的枚举。如果这找到了一个新的解决方案,我们可以将它添加到我们的解决方案中,然后重复目标的另一个新副本,迫使它枚举另一个新的解决方案,依此类推:

stupid_setof(Template, Goal, SolutionsSeen, Set) :-
    copy_term(Goal-Template, GoalInstance-Solution),
    call(GoalInstance),
    \+ member(Solution, SolutionsSeen),
    !, 
    stupid_setof(Template, Goal, [Solution | SolutionsSeen], Set).

如果GoalN个答案,这将按N**2个的顺序枚举,并在解决方案列表中进行相应的线性搜索。它还将执行 Goal 多次出现的任何副作用。

但它“有效”:

?- stupid_setof(X, foo(X), Xs).
Xs = [a, b, c].

而且,尽管它很愚蠢,但如果 Goal 没有解决方案,这仍然比标准 setof/3 更愚蠢:

:- dynamic bar/1.  % no clauses

?- setof(X, bar(X), Set).
false.

?- stupid_setof(X, bar(X), Set).
Set = [].