首页 > 解决方案 > Prolog:列出事实

问题描述

可以说我正在寻找某人的朋友。

friend(mary, peter).
friend(mary, paul).
friend(mary, john).
friend(mary, justin).

friend(chris, peter).
friend(chris, paul).
friend(chris, conner).
friend(chris, louis).

friend(tyler, justin).
friend(tyler, lindsey).
friend(tyler, frank).
friend(tyler, paul).

friend(dan, justin).
friend(dan, conner).
friend(dan, frank).
friend(dan, peter).

在上面的例子中,我知道我可以做类似的事情

friend(X, paul).

这会告诉我每个人都是保罗的朋友

或者也

findall(X, friend(X, paul), L).

这将返回一个 Paul 是朋友的每个人的列表。

假设我想更深入地搜索并完善它。所以让我们说我要做

findall(X, friend(X, paul), A).

让 A 成为 [mary, chris, tyler] 的列表。但后来我想进一步完善该搜索。我希望能够将列表放入函数中(我知道这无效,但这是我想要做的事情的思维模式),例如

findall(A, friend(A, peter), B).

这次回来只是[玛丽,克里斯]。然后甚至进一步细化它说

findall(B, friend(B, conner), C).

终于结束了我的寻找[克里斯]

这可能吗?有没有更好甚至可能的方法来做到这一点?

标签: prolog

解决方案


连锁条件

鉴于我理解正确,您想要生成与和为朋友的paul人的列表。我们可以通过“构建”一个旨在满足三个条件的目标来做到这一点:peterconnor

findall(F, (friend(F, paul), friend(F, peter), friend(F, conner)), L).

或其他条件的结合。然后这些给出:

?- findall(F, (friend(F, paul)), L).
L = [mary, chris, tyler].

?- findall(F, (friend(F, paul), friend(F, peter)), L).
L = [mary, chris].

?- findall(F, (friend(F, paul), friend(F, peter), friend(F, conner)), L).
L = [chris].

member/2在目标中过滤现有列表

或者我们也可以使用两个调用,并用于member/2“模拟”一个交叉点,例如:

findall(F, friend(F, paul), L1),
findall(F, (member(F, L1), friend(F, peter)), L2).

因此,在这里我们使用member/2这样的方法,F2它只从L1(初始值的结果findall/3)中获取值,但我认为第一种方法更清晰,更自我解释:我们毕竟想要一个与, 和, 和F是朋友的 s列表。paulpeterconner

用于maplist/2获取与列表中的每个人都是朋友的人

maplist/2我们还可以使用此处找到与人员列表成为朋友的人:

findall(F, maplist(friend(F), [paul, peter, conner]), L).

具有交叉点的后处理

我们也可以分别进行两次findall/3调用,然后计算交集:

findall(F, friend(F, paul), L1),
findall(F, friend(F, paul), L2),
intersection(L1, L2, M).

因此,此处M将包含同时位于L1和中的元素L2。但是这里的一个潜在问题是,第二个谓词可能会产生很多结果,而如果我们使用第一个谓词的值进行过滤,这是不可能的。


推荐阅读