例如,如果我有一个 Prolog 谓词,例如 a(A, B)。
bagof/3
在给定 A 的值的情况下,是否可以在不使用内置谓词(例如or )的情况下将 B 的所有在谓词 a 之后的值收集到一个列表中findall/3
。
例如,如果我有一个 Prolog 谓词,例如 a(A, B)。
bagof/3
在给定 A 的值的情况下,是否可以在不使用内置谓词(例如or )的情况下将 B 的所有在谓词 a 之后的值收集到一个列表中findall/3
。
你有两个明显的选择(对我来说很明显;似乎还有更多)。一是确实使用数据库来保存状态。这至少有一个缺陷:根据您决定为临时状态使用的名称,您可能会破坏程序保持的其他状态。这是所有语言都遭受的相同的旧“全局状态”/“全局变量”问题。
另一种选择是使用“局部变量”和非回溯赋值来保持临时状态。这很可能取决于实现。对于初学者,您可以查看nb_setarg/3
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
回溯,直到它给我们一个我们以前没有见过的解决方案。请注意,这些查询是独立的:在每个查询中,我们都有一个全新的foo(X)
目标副本,从a
.
我们可以通过在调用它之前复制原始目标以编程方式执行相同的操作,强制它从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).
如果Goal
有N
答案,这将枚举它们的顺序N**2
并在解决方案列表中进行相应的线性搜索。Goal
它还将执行具有多次的任何副作用。
但它“有效”:
?- stupid_setof(X, foo(X), Xs).
Xs = [a, b, c].
setof/3
而且,尽管它很愚蠢,但如果Goal
没有解决方案,这仍然没有标准那么愚蠢:
:- dynamic bar/1. % no clauses
?- setof(X, bar(X), Set).
false.
?- stupid_setof(X, bar(X), Set).
Set = [].