Prolog - 划分N个部分的列表

时间:2012-01-03 13:10:53

标签: list prolog dcg divide

我正在尝试编写一个将列表分为N个部分的谓词。 这就是我到目前为止所拥有的。

partition(1, List, List).
partition(N, List, [X,Y|Rest]):-
    chop(List, X, Y),
    member(NextToChop, [X,Y]), %Choose one of the new parts to chop further.
    NewN is N-1,
    partition(NewN, NextToChop, Rest).

chop(List, _, _):-
    length(List, Length),
    Length < 2, %You can't chop something that doesn't have at least 2 elements
    fail,!.
chop(List, Deel1, Deel2):-
    append(Deel1, Deel2, List),
    Deel1 \= [],
    Deel2 \= [].

我的想法是将列表中的部分切成另外两个部分,直到我有N个部分。 我用这种方法得到了平庸的结果:

?- partition(2, [1,2,3,4], List).
List = [[1], [2, 3, 4], 1] ;
List = [[1], [2, 3, 4], 2, 3, 4] ;
List = [[1, 2], [3, 4], 1, 2] ;
List = [[1, 2], [3, 4], 3, 4] ;
List = [[1, 2, 3], [4], 1, 2, 3] ;
List = [[1, 2, 3], [4], 4] ;
false.

所以我得到了我想要的东西,但是我得到了两次并且还有其他一些东西。 分成3部分时情况会变得更糟:

?- partition(3, [1,2,3,4], List).
List = [[1], [2, 3, 4], [2], [3, 4], 2] ;
List = [[1], [2, 3, 4], [2], [3, 4], 3, 4] ;
List = [[1], [2, 3, 4], [2, 3], [4], 2, 3] ;
List = [[1], [2, 3, 4], [2, 3], [4], 4] ;
List = [[1, 2], [3, 4], [1], [2], 1] ;
List = [[1, 2], [3, 4], [1], [2], 2] ;
List = [[1, 2], [3, 4], [3], [4], 3] ;
List = [[1, 2], [3, 4], [3], [4], 4] ;
List = [[1, 2, 3], [4], [1], [2, 3], 1] ;
List = [[1, 2, 3], [4], [1], [2, 3], 2, 3] ;
List = [[1, 2, 3], [4], [1, 2], [3], 1, 2] ;
List = [[1, 2, 3], [4], [1, 2], [3], 3] ;
false.

另一个想法是使用前缀,但我不知道这将如何工作。要使用它,我应该能够让Prolog知道它需要一个不太短而且不太长的前缀,所以我不会采用太长的前缀,所以没有任何东西可以用于下一个递归步骤。

有人能指出我正确的方向吗?

很少澄清:谓词应该返回在N个部分(不包括空列表)中划分列表的所有可能性。

2 个答案:

答案 0 :(得分:5)

在描述涉及列表的关系时,DCG通常非常有用。考虑:

list_n_parts(List, N, Parts) :-
        length(Parts, N),
        phrase(parts(Parts), List).

parts([]) --> [].
parts([Part|Parts]) --> part(Part), parts(Parts).

part([P|Ps]) --> [P], list(Ps).

list([]) --> [].
list([L|Ls]) --> [L], list(Ls).

示例查询:

?- list_n_parts([1,2,3,4], 2, Ps).
Ps = [[1], [2, 3, 4]] ;
Ps = [[1, 2], [3, 4]] ;
Ps = [[1, 2, 3], [4]] ;
false.

答案 1 :(得分:3)

以下是我用来实现该方法的基本方法(使用append/2length/2):

list_n_parts(List, Parts, Result) :-
    length(Result, Parts),
    append(Result, List).

现在,这并不完全符合您的期望:它允许[]

要解决此问题的一个想法是使用maplist调用事先格式化结果列表:

list_n_parts(List, Parts, Result) :-
    length(Result, Parts),

使用copy_term/2maplist/2调用如下:

    maplist(copy_term([_|_]), Result),

使用functor/3(信用@false),它看起来像:

    maplist(functor('.', 2), Result),

使用lambda.pl你可以写:

    maplist(\[_|_]^true, Result),

因为'\'已经执行了术语副本(感谢@false)。

唯一剩下的就是append/2电话:

    append(Result, List).

另一个想法是使用forall/2过滤(可能更容易获得,但复杂性更差):

list_n_parts(List, Parts, Result) :-
    length(Result, Parts),
    append(Result, List),
    forall(member(X, Result), X \= []).

等...