【问题标题】:Prolog - dividing a list in N partsProlog - 将列表分成 N 部分
【发布时间】:2012-01-03 13:10:53
【问题描述】:

我正在尝试编写一个将列表分成 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 部分的所有可能性(不包括空列表)。

【问题讨论】:

  • N个部分应该有相同的长度吗?谓词是否应该返回所有可以将列表分成 N 部分的方法?
  • @thanosQR:谓词应该返回所有可能的方式,你可以将列表分成 N 部分。我会把它添加到 OP 中。

标签: list prolog dcg divide


【解决方案1】:

在描述涉及列表的关系时,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.

【讨论】:

  • 我在另一个班级看到过 DCG,但从不知道你可以在 Prolog 中使用这些。您的解决方案非常干净,但由于我在声明性语言课程中没有看到 DCG,我将使用 @Mog 的解决方案。无论如何,谢谢,我在 Prolog 中了解了 DCG ^^
【解决方案2】:

这是我用来实现它的基本方法(使用 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 \= []).

等等……

【讨论】:

  • 啊该死的,就是这么简单,我什至无法理解它。非常感谢! @mat 的解决方案也很好很干净,但它使用了我在课堂上没有看到的东西,所以我会使用你的:)
  • @Mog: maplist(\[_|_]^true, Result) 的描述与maplist(\X^(copy_term([_|_], NotEmpty), =(NotEmpty, X)), Result) 相同。也就是说,单独的\ 已经执行了copy_term
  • 那种的情况下,maplist(functor('.',2), Result) 仍然是可取的,但幅度很小。
  • @Mog:很遗憾你将 lambda 版本一并删除。
  • 我会删除分散注意力的 cmets - 这对其他人来说并不有趣。想一想:如果有人会在 3 年内读到这篇文章。另外,你知道如何从程序链接吗?
猜你喜欢
  • 2014-06-29
  • 2023-04-10
  • 1970-01-01
  • 1970-01-01
  • 2016-09-27
  • 1970-01-01
  • 2011-07-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多