使用Prolog将单个列表拆分为三个

时间:2014-11-21 08:19:47

标签: prolog failure-slice

我正在尝试创建一个函数,将可变长度列表按顺序拆分为三个偶数长度列表。以下将其拆分为三个,但进程一次一个地将它们插入到每个列表中。

我想要的一个例子是:

[1, 2, 3, 4, 5] -> [1, 2], [3, 4], [5]

另一个例子是:

[8, 7, 6, 5, 4, 3, 2, 1] -> [8, 7, 6], [5, 4, 3], [2, 1].

以下代码通过一次插入一个列表来分割它们:

div([], [], [], []).
div([X], [X], [], []).
div([X,Y], [X], [Y], []).
div([X,Y,Z|End], [X|XEnd], [Y|YEnd], [Z|ZEnd]):-
  div(End, XEnd, YEnd, ZEnd).

此代码输出:

[1, 2, 3, 4, 5] -> [1, 4], [2, 5], [3]

如何解决此问题?

2 个答案:

答案 0 :(得分:5)

@Boris的答案在第一个参数列表的长度未知时不会终止。要看到这一点,我们无需再使用来查看第一个目标:

div(L, L1, L2, L3) :-
    length(L, Len), false,
    % here you compute for example Len1 and Len2
    length(L1, Len1),
    length(L2, Len2),
    append(L1, L1_suffix, L),
    append(L2, L3, L1_suffix).

另一方面,您的原始程序有quite nice termination properties。 cTI给出了以下最佳终止特性:

div(A,B,C,D) terminates_if b(A);b(B);b(C);b(D).

换句话说,为确保终止,您只需要一个参数(ABCD)作为具体列表有限的和地面的(这就是b(..)的含义)。这是一个非常强大的终止条件。这些论点不合适真的很可惜!为什么不概括你的程序?唯一的问题就是它限制了列表元素。所以我将用_ s替换列表元素的所有变量名:

gdiv([], [], [], []).
gdiv([_], [_], [], []).
gdiv([_,_], [_], [_], []).
gdiv([_,_,_|End], [_|XEnd], [_|YEnd], [_|ZEnd]):-
  gdiv(End, XEnd, YEnd, ZEnd).

very same终止属性适用于此程序。

唉,现在有点太笼统了。鲍里斯的解决方案现在可以改变用途:

divnew(Zs, As, Bs, Cs) :-
   gdiv(Zs, As, Bs, Cs),
   append(As, BsCs, Zs),
   append(Bs, Cs, BsCs).

我表达同样的首选方式是:

divnew(Zs, As, Bs, Cs) :-
   gdiv(Zs, As, Bs, Cs),
   phrase( ( seq(As), seq(Bs), seq(Cs) ), Zs).

有关seq//1的定义,请参阅other answers

答案 1 :(得分:2)

div(L, L1, L2, L3) :-
    append(L1, L1_suffix, L),
    append(L2, L3, L1_suffix).

你看到这如何拆分三个名单?现在,您不能说明您希望列表L1L2L3有多长。您可以使用length/2获取L的长度,并设置三个结果的长度,如果您不希望谓词像目前那样一般。

因为你说"相对均匀的长度",这是相对的,我需要以某种方式解释它,让我们假设你的意思是,对于正整数len和n,len = 3n,你得到len1 = len2 = len3 = n,对于k = 3n + 1,你得到len1 = n + 1,len2 = len3 = n,对于k = 3n + 2,你得到len1 = len2 = n + 1,len3 = n。我让你弄清楚如何计算长度。

div(L, L1, L2, L3) :-
    length(L, Len),
    % here you compute for example Len1 and Len2
    length(L1, Len1),
    length(L2, Len2),
    append(L1, L1_suffix, L),
    append(L2, L3, L1_suffix).