如何在 Prolog 列表中打包元组元素

How do I bag elements of tuples in Prolog lists

假设我有一个列表:

Os = [(4,P1),(9,P2),(4,P3),(1,P4),(9,P5)].

我想将元组的每个第二个元素都放在一个具有相同第一个元素的包中,如下所示:

SortedOs = [(4,[P1,P3]),(9,[P2,P5]),(1,P4)].

目前我正在使用 bagof/3:

findall(
    (O,Bag),
    bagof(P,member((O,P),Os),Bag),
    SortedOs
).

但是它给了我这样的排序列表:

SortedOs = [(1,P4),(4,[P1,P3]),(9,[P2,P5])]

表示bagof/3按升序查找第一个元素。有什么办法可以改变它以获得我想要的列表吗?非常感谢。

如果您使用的是 SWI-Prolog,您可以将数据格式从元组更改为 Pairs, (Key-Value) then you can use group_pairs_by_key/2

请注意,要使 group_pairs_by_key/2 正常工作,必须对输入列表进行排序。

代码

example(Pairs0,Result) :-
    sort(Pairs0,Pairs1),
    group_pairs_by_key(Pairs1,Result).

用法示例

?- Pairs = [4-P1,9-P2,4-P3,1-P4,9-P5],example(Pairs,Result).
Pairs = [4-P1, 9-P2, 4-P3, 1-P4, 9-P5],
Result = [1-[P4], 4-[P1, P3], 9-[P2, P5]].


SWI-Prolog 有一个用于处理有序集(排序列表)的谓词库。 ordsets.pl -- 有序集合操作

这是一个检查输入是否为有序集的变体,如果是有序集则跳过排序。

example(List,Result) :-
    (
        is_ordset(List)
    ->
        Order_set = List
    ;
        list_to_ord_set(List,Order_set)
    ),
    group_pairs_by_key(Order_set,Result).

用法示例

?- List = [1-P4, 4-P1, 4-P3, 9-P2, 9-P5],example(List,Result).
List = [1-P4, 4-P1, 4-P3, 9-P2, 9-P5],
Result = [1-[P4], 4-[P1, P3], 9-[P2, P5]].

?- List = [4-P1,9-P2,4-P3,1-P4,9-P5],example(List,Result).
List = [4-P1, 9-P2, 4-P3, 1-P4, 9-P5],
Result = [1-[P4], 4-[P1, P3], 9-[P2, P5]].

注意:有序集删除重复项,因此使用 list_to_ord_set/2 而不是 sort/2 将删除重复项。

?- List = [4-P1,4-P1,9-P2,4-P3,1-P4,9-P5],example(List,Result).
List = [4-P1, 4-P1, 9-P2, 4-P3, 1-P4, 9-P5],
Result = [1-[P4], 4-[P1, P3], 9-[P2, P5]].

group_pairs_by_key/2 (source code)