在 prolog 中绑定无序列表

Binding unordered lists in prolog

我对 prolog 有点陌生,我想弄清楚如何在顺序无关紧要的情况下实现绑定列表的元素。我在我的代码的最后两行中证明了这一点。这样做似乎微不足道,但它无法绑定。

fh(Hand) :-
   is_card(R, _, C1),
   is_card(R, _, C2),
   is_card(R, _, C3),
   C1 \== C2,
   C2 \== C3,
   C1 \== C3,

   is_card(RR, _, D1),
   is_card(RR, _, D2),
   R \= RR,
   D1 \== D2,
   is_set(Hand),
   list_to_set([C1,C2,C3,D1,D2], Hand).

我希望得到一些提示。 (隐藏谓词名称和功能以防止抄袭)

阅读 library(lists) 中可用的文档,似乎对于您的用例,最好将集合表示为没有重复项的排序列表,使用 sort/2.

这是我得到的:

?- A = [2,3,1],
   B = [1,1,2,3,2,3],
   sort(A, S),
   sort(B, S).
A = [2, 3, 1],
B = [1, 1, 2, 3, 2, 3],
S = [1, 2, 3].

sort(A, S) 将确保 S 是一个与 A 具有相同元素的有序列表,而 sort(B, S) 将确保如果您从 B 中创建一个集合,则它与 A 相同。

我看到 is_set/1 的实现可能与 length(Set, Lenght), sort(Set, Sorted), length(Sorted, Len). 相同,但我找不到 '$skip_list' 的定义。但是我看到了:

?- '$skip_list'(Len, [2,3,1], Tail).
Len = 3,
Tail = [].

与其他评论者一样,我假设您使用的是 SWI-Prolog 库中的 is_set/1。在你的代码中你有一个电话

is_set(Hand)

其中 Hand 是一个自由变量。您的目标似乎是将其用作一种生成器或“类型声明”,但这不起作用:

?- is_set(X).
false.

documentation of is_set/1部分内容如下:

is_set(@Set) [det]

True if Set is a proper list without duplicates.

这里发生了一些事情。根据 SWI's mode documentationdet 表示“在没有选择点的情况下只成功一次”,而 @ 表示“参数不会比调用时进一步实例化”。将这两者放在一起,is_set/1 不能 成为生成器,因为它不枚举解决方案:它甚至不将其参数实例化为单个解决方案 (@ ),即使成功了,也不会成功多次 (det)。 (事实上​​,det 谓词永远不会失败,但 is_set/1 会失败;这似乎是一个文档错误,正确的注释应该是 semidet。)

因此:您不能将 is_set/1 用作生成器,只能用作类型检查。但即使作为类型检查,它在这里也无济于事,因为自由变量肯定 不是 “适当的列表”。但是,您在这里不需要生成器或类型检查。删除 is_set/1 调用,list_to_set/2 单独应该做你想做的事。

您可以用更严格的条件构建您的手牌,通过对它们下订单而不是仅仅检查它们是否不同来建立独特的元素:

fh(Hand) :-
   is_card(R, _, C1),
   is_card(R, _, C2),
   is_card(R, _, C3),
   C1 @< C2,
   C2 @< C3,

   is_card(RR, _, D1),
   is_card(RR, _, D2),
   dif(R, RR),
   D1 @< D2.

这样,您将使用排序来构建您的手牌,这样您就不会出现任何重复。由于您不关心顺序是什么,因此拥有特定顺序不会干扰需求并提供一种建立唯一性的方法。