将列表拆分为排序的子列表(Prolog)

时间:2019-05-01 04:38:36

标签: prolog

如何将列表[5,4,2,8,3,1,6,9,5]划分为多个子列表,这些子列表将按破坏序列的值进行拆分?

例如,列表[5,4,2,8,3,1,6,9,5]会产生像[5], [4], [2,8], [3], [1,6,9], [5] or [[5], [4], [2,8], [3], [1,6,9], [5]]这样的子列表的列表(没关系)。

关于此的任何想法或建议如何解决此问题?

谢谢。

3 个答案:

答案 0 :(得分:2)

在我看来,这似乎是DCG的问题,所以这是一个DCG解决方案:

ascending([X|Xs]) -->
    [X],
    ascending(X, Xs).

ascending(X, [Y|Xs]) -->
    [Y],
    { X =< Y },
    { ! },
    ascending(Y, Xs).
ascending(_X, []) -->
    [].

可以与phrase/3一起使用,以获取排序的前缀和其余元素:

?- phrase(ascending(Asc), [1,2,3,4,5], Rest).
Asc = [1, 2, 3, 4, 5],
Rest = [].

?- phrase(ascending(Asc), [1,2,3,4,5,2,3,4,5], Rest).
Asc = [1, 2, 3, 4, 5],
Rest = [2, 3, 4, 5].

?- phrase(ascending(Asc), [1,2,3,4,5,2,3,4,5], Rest), phrase(ascending(Asc2), Rest, Final).
Asc = [1, 2, 3, 4, 5],
Rest = Asc2, Asc2 = [2, 3, 4, 5],
Final = [].

主要谓词是:

sorted_sublists([], []).
sorted_sublists(List, [Prefix|Remaining]) :-
    phrase(ascending(Prefix), List, Rest),
    sorted_sublists(Rest, Remaining).

但是,ascending//2中的剪切有点难看。 DCG中的否定有点乏味,但可以使其受约束:

:- use_module(library(clpfd)).

ascending(X, [Y|Xs]) -->
    { X #=< Y },
    [Y],
    ascending(Y, Xs).
ascending(X, []) -->
    { X #=< Y },
    \+ [Y].

我认为这很好。有某种方法可以做类似但不一定有约束的事情吗?本质上,一种写DCG的方式“匹配空列表或不满足某些谓词P的非空列表”?

答案 1 :(得分:1)

非常棘手,但是可以使用DCG来完成:

sub([A,B|T]) --> [[A]], {A > B},sub([B|T]); {A =< B, phrase(sub_1([A,B|T], S), R, [])}, [R], sub(S).
sub([A]) --> [[A]].
sub([]) --> [].
sub_1([A,B|T], S) --> [A], {A =< B},  sub_1([B|T], S);[A], {A > B, S =  [B|T]}.
sub_1([A], []) -->  [A].

结果:

 ?- phrase(sub([5,4,2,8,3,1,6,9,5] ), A, []).
A = [[5], [4], [2, 8], [3], [1, 6, 9], [5]] ;
false

答案 2 :(得分:0)

您只是想提出一项策略,但是我真的无法提出一个好的策略。我希望其他人能比我的方法更好。

我真的对我的解决方案不满意,因为感觉像是一个问题,这个简单的问题应该得到一个简单的解决方案,而我的解决方案不是很简单。实际上,我觉得这样的事情应该起作用:

sorted_sublists([], []).
sorted_sublists(L, [Prefix|Remaining]) :-
   append(Prefix, Suffix, L),
   sort(Prefix, Prefix),
   sorted_sublists(Suffix, Remaining).

这对我来说似乎是合理的说明:给我一个前缀L,如果已经对它进行了排序,则将其放在结果列表中,然后继续查找剩余的内容。但是,这不起作用,因为Prefix可以是空列表,但是如果您这样修复它:

sorted_sublists([], []).
sorted_sublists(L, [Prefix|Remaining]) :-
    append(Prefix, Suffix, L),
    Prefix=[_|_],
    sort(Prefix, Prefix),
    sorted_sublists(Suffix, Remaining).

它仍然不起作用,因为您得到了很多解决方案,而其中的 last 是您真正想要的解决方案:

[debug]  ?- sorted_sublists([1,2,3,1,2,1,1], Sublists).
Sublists = [[1], [2], [3], [1], [2], [1], [1]] ;
Sublists = [[1], [2], [3], [1, 2], [1], [1]] ;
Sublists = [[1], [2, 3], [1], [2], [1], [1]] ;
Sublists = [[1], [2, 3], [1, 2], [1], [1]] ;
Sublists = [[1, 2], [3], [1], [2], [1], [1]] ;
Sublists = [[1, 2], [3], [1, 2], [1], [1]] ;
Sublists = [[1, 2, 3], [1], [2], [1], [1]] ;
Sublists = [[1, 2, 3], [1, 2], [1], [1]] ;
false.

仍然,好像是朝正确方向运动。如果我们有一个可以剥离第一个排序前缀的谓词呢?如果有,我们可以跳过append/3和错误的解决方案。因此,让我们专注于编写该谓词。我想到了这个:

sorted_prefix([Last], [Last], []).
sorted_prefix([X,Y|Ys], Prefix, Suffix) :-
    (X < Y ->
        sorted_prefix([Y|Ys], Prefix0, Suffix),
        Prefix = [X|Prefix0]
    ;
        Prefix = [X], Suffix = [Y|Ys]
    ).

因此,基本情况是列表中只有一个元素。那是一个排序的前缀。

归纳式案例比较棘手。这样的想法是,如果前两个项目按顺序排列,那么我想重述第二个项目以及其余列表,然后将结果放在该结果的前面。换句话说,如果L的排序前缀为R并且X小于L的第一项,则[X | L]的排序前缀为[X | R]。如果不是这种情况,我们将在另一种情况下结束,即如果X大于L的第一个元素,则[X | L]的排序前缀仅为[X]。在这种情况下,我们还必须计算出后缀,即L。

最后的sorted_sublists/2变得更简单:

sorted_sublists([], []).
sorted_sublists(L, [Prefix|Remaining]) :-
    sorted_prefix(L, Prefix, Suffix),
    sorted_sublists(Suffix, Remaining).

这只是一次递归剥离一个排序的前缀。