【问题标题】:Properly working with sets in Prolog在 Prolog 中正确使用集合
【发布时间】:2025-12-02 18:10:01
【问题描述】:

在 Prolog 中,似乎集合是使用列表来表示的。

例如,这里是来自 SWI-Prolog 的 union/3 的实现:

union([], L, L) :- !.
union([H|T], L, R) :-
    memberchk(H, L), !,
    union(T, L, R).
union([H|T], L, [H|R]) :-
union(T, L, R).

但是这个谓词不是很具有声明性,例如:

?- union([1,2,3],X,[1,2,3,4]).
X = [1, 2, 3, 4].

应该留下一些选择点,或者:

?- union(X,[1,2],[1,2,3,4]).
<infinite loop>

这根本行不通!

除此之外,我们还发现以下问题:

?- union([1,2,3],[4,5],[1,2,3,5,4]).
false.

?- union([1,1,1],[4,5],[1,1,1,4,5]).
true.

如果我们真的要谈论集合,那显然是错误的。

我们可以清楚地看到,使用列表来讨论集合并不简单,因为:

  • 集合不是有序的,而列表是有序的;
  • 集合不包含重复值,而列表可以。

因此,我们要么找到适用于集合的谓词,从而削减可能的解决方案(例如这种联合的实现,它仅在集合是有序的情况下才有效),要么根据变量的实例化提供无用的选择点的谓词(例如,联合谓词将具有与结果集的排列数一样多的选择点)。

应该如何在 Prolog 中正确实现适用于集合的谓词?

这个问题非常笼统,并不针对这里使用的union/3 的例子。

【问题讨论】:

  • 现在懒得回答了,但你至少应该看看 library(ordsets)。您还可以使用在 library(assoc) 和 SWI-Prolog 的 dict 中实现的“查找表”作为一组:使用键,所有值都可以是原子 true 或类似的东西。它们没有为您的问题提供现成的答案,但至少它们是比您展示的更好的集合实现。

标签: list prolog set unification


【解决方案1】:

如果您想要非常笼统的概念,则必须使用自己的统一算法实现自己的数据类型。与您的previous question 相比,AC 统一比纯关联统一“简单得多”。您“仅”必须解决丢番图方程等。交流统一的文献比联想统一的文献多得多。

但这实际上更像是一个研究项目,而不是编程任务。今天你可以在纯 Prolog 中做什么?

只要考虑到函数依赖关系,您就可以以一种仍然纯粹且声明性的方式使用列表来近似集合。更多信息请见this answer

【讨论】:

    【解决方案2】:

    应该如何正确地实现适用于集合的谓词 序言?

    首先,prolog 中的联合谓词应该尊重集合联合的基本数学属性,因此它是:

    • 关联性:A ∪ (B ∪ C) = (A ∪ B) ∪ C
    • 交换:A ∪ B = B ∪ A

    (这些属性确保联合是明确的,但不应该涉及带有 to 参数的 Prolog 谓词实现。)

    此外,联合实现(或其他集合谓词)在 Prolog 中还应具有以下属性:

    • Handle duplicates

    如果其中一个列表至少有一个元素超过一次,那么这个元素应该只计算一次。

    • Handle cases where at least one argument is not instantiated.

    例如Union([X],Union_Set,[Y]). 显然应该返回Union_set=[X,Y]. 另一个例子:Union([X],[X1,Y1],[Y]). 显然应该通过统一返回X1=X, Y1=Y.

    • Be deterministic 并集是集合中所有不同元素的集合。这个定义是明确定义的(数学上),它不会留下非唯一性选项(对于每个明确定义的数学运算,结果必须是唯一的(不仅是联合)。
    • 另一个需要的特征可能是由代数属性(交换性、结合性)提供的逻辑纯度。
    • 处理无限循环情况。

    正如您在联合谓词中的示例:union(X,[1,2],[1,2,3,4])。应该返回一些实例化错误。

    这些是我应该包括的一些特性,因为我们正在讨论 Set 操作,但当然这些并不是我们可以考虑的所有属性。这也与我们在定义谓词时所做的实现有关。

    最后再评论:Sets are not ordered whereas lists are;

    这不是真的。部分排序或全排序适用于列表和集合,我们是否可以比较所有元素或仅比较部分元素,这意味着我们可以将它们按顺序排列。任何像列表这样的数据结构都不提供顺序(顺序与语义有关),除非我们将其视为例如在堆中,它是一个树结构,但我们认为它是有序的。

    【讨论】:

    • 如果您喜欢结合性和交换性等代数性质,您将不可避免地已经接受了逻辑纯洁性。因为,没有它,这些属性将不会实现!
    • 确实很有用的评论@false,谢谢!!!如果我提到逻辑纯度,我会编辑答案。
    【解决方案3】:

    首先,添加一个我们目前必须处理的额外示例:

    ?-联合(A,[],A)。 A = []

    我们可以读作:

    空集是only集。

    谁会想到?


    ECLiPSe 中提供了一个非常好的推理集合库 library(conjunto)

    Conjunto 是一个解决有限集域项上的集约束的系统。它是使用基于元术语的 ECLiPSe 内核开发的。它包含 ECLiPSe 的有限域库。库 conjunto.pl 实现了对包含 herbrand 术语和基础集的集合域术语的约束。

    另请注意:

    从 ECLiPSe 5.1 版开始,本章中描述的库将被新的集合求解器库 lib(ic_sets) 逐步淘汰并取代。

    这些都是很棒的库,如果您对设置约束感兴趣,我建议您将它们作为起点。

    一个很好的例子可以通过设置约束来完成:

    http://csplib.org/Problems/prob010/models/golf.ecl.html

    【讨论】:

    • 对第一个示例的解释略有不同:只有空集与空集统一。
    • 另一方面,这个专业化成功了:?- A = [a], union(A, [], A).
    • 确实,我们无法对整个定义做出任何有意义的解释。我们只能给出一个具体的答案。
    • csplib.org 链接现在需要“www.”:csplib.org/Problems/prob010/models/golf.ecl.html。如果它再次中断,那就是“社交高尔夫球手问题”