根据条件在序言中拆分列表

Splitting list in prolog based on condition

我有一个列表

[a,a,a,a,b,b]

我的objective是拆分成

[[a,a,a,a],[b,b]]

到目前为止我已经实现的谓词

split1([],[]).
split1([A1|T],[[H1,H2]|B]):-
A1=[H1,H2],
H1=H2,
split(T,B).

split1([],[]).
split1([A1|T],[[H1,H2]|B]):-
A1=[H1,H2],
H1\=H2,
split(T,B).

对于新手问题学习序言,抱歉。

这与 非常相似。下面是相关思路。

您将需要保留一个 运行 列表 重复元素,因为您的结果有计数器。因此,考虑一个包含当前 运行 重复列表的辅助谓词。这种 "running list" 在 Prolog 中很常用,被称为 累加器 .

split(L, C) :-
    split(L, [], C).   % Start repeat list accumulator at []

现在您需要考虑几种不同的情况:

split([], _, []).                 % This is an easy one!

这是说如果我压缩一个空列表,我得到一个空列表。

split([H], A, [[H|A]]).           % This is an easy one!

这个说如果我收集一个元素的列表并且我当前的 运行 重复列表是 A,那么结果是 [[H|A]]

split([H, H|T], A, TC) :-
    ...

这是我有一个 运行 重复列表 A 并且元素仍在重复的情况。结果将是一个列表 TC 但我还不知道它是什么样子,因为我们仍处于重复循环中,需要通过递归来确定。这个谓词应该是什么样的?本子句中需要的对 split1 的递归调用将有一个新的累加器列表。它看起来像什么?

split([H1, H2|T], A, [[H1|A]|TC]) :-
    dif(H1, H2),
    ...

这是我有一个 运行 重复列表 A 并且重复停止在 H1 的情况。由于当前循环的重复以H1结束,我们知道结果看起来像[[H1|A]|TC],因为重复循环以H1结束,整个重复列表是H1带有尾部 A(它只是将 H1 添加到 A 之前,它们都是相同的元素)。我们还没有通过递归确定列表的其余部分TC。这个谓词实现应该是什么样的?

还有其他方法可以实现上述逻辑(例如,使用 ->; 构造等),但这将使事情变得简单。

尝试将这些视为规则,其中谓词子句的头部是断言,如果子句的以下元素为真,则该断言为真。并递归思考。


作为事后的想法,这可以在没有单独的累加器的情况下通过使用结果来携带累加器来完成:

split([], []).
split([H], [[H]]).
split([H1,H2|T], [[H1]|R]) :-
    dif(H1, H2),
    split(...).                 % left as an exercise
split([H,H|T], [[H|TH]|R]) :-
    split(...).                 % left as an exercise

你应该看看SWI-Prolog implementation of group_pairs_by_key/2。它做的比你需要的多一点,但我想省去不必要的部分很容易。

使用目前的谓词,您可以:

?- L = [a,a,b,b,a,b,b],
   keys_values_pairs(L, L, P),
   group_pairs_by_key(P, G),
   keys_values_pairs(_, Result, G).
L = [a, a, b, b, a, b, b],
P = [a-a, a-a, b-b, b-b, a-a, b-b, b-b],
G = [a-[a, a], b-[b, b], a-[a], b-[b, b]],
Result = [[a, a], [b, b], [a], [b, b]].

所以您只需要弄清楚要遗漏什么以获得不需要 键和值,而只需要单个值的谓词。由于这显然是家庭作业,而且您甚至没有在答案上付出足够的努力,所以我将把它留作练习。

您只需要splitlistIfAdj/3 and dif/3。示例使用:

?- splitlistIfAdj(dif, [a,a,a,b,b,c,a,a,a,a], Xss).
Xss = [[a,a,a],[b,b],[c],[a,a,a,a]].

一个简单的定义:

groups([X|Xs],[G|Gs]) :- take(X,Xs,G,Rs), groups(Rs, Gs).
groups([],[]).

take(X,[X|R],[X|G],S) :- !, take(X,R,G,S).
take(X,R,[X],R).

take/4 消耗并存储匹配的元素,将剩余部分留给 group