Prolog - 将列表分为3部分

时间:2011-05-22 15:37:59

标签: list split prolog

我试图将Prolog中的列表分成3个相等的部分(......尽可能相等)。 我的算法如下:

  1. 找出初始列表的大小。
  2. 使用两个额外参数调用该过程(列表的大小和计数器将告诉我何时应该停止向一个列表添加元素并开始添加到另一个列表中)
  3. 程序如下:

    有4个参数:

    div3(InitialList,FirstNewList,SecondNewList,ThirdNewList).
    

    有2个额外参数:

    div3(InitialList,FirstList,SecondList,ThirdList,InitialListSize,Counter).
    

    这是我的代码:

    div3([],[],[],[]).
    div3([X],[X],[],[]).
    div3([X,Y],[X],[Y],[]).
    div3([X,Y,Z],[X],[Y],[Z]).
    div3([X | Y],A,B,C) :- length([X | Y],Sz),
                           Sz1 is 0,
                           div3([X | Y],A,B,C,Sz,Sz1).
    
    div3([X | Y],A,B,C,Sz,Sz1) :- Sz1 < Sz//3, % am I done adding to the 1st list?
                                  append(X,L,A), % add to the 1st list
                                  Sz2 is Sz1+1, % increment the counter
                                  div3(Y,L,B,C,Sz,Sz2),!.
    
    div3([X | Y],A,B,C,Sz,Sz1) :- Sz1 < 2*Sz//3, % am I done adding to the 2nd list?
                                  append(X,L,B), % add to the 2nd list
                                  Sz2 is Sz1+1, % increment the counter
                                  div3(Y,A,L,C,Sz,Sz2),!.
    
    div3([X | Y],A,B,C,Sz,Sz1) :- Sz1 < Sz, % am I done adding to the 3rd list?
                                  append(X,L,C),% add to the 3rd list
                                  Sz2 is Sz1+1, % increment the counter
                                  div3(Y,A,B,L,Sz,Sz2),!.
    

3 个答案:

答案 0 :(得分:1)

我认为代码的第一部分几乎是正确的...... 你正在寻找的是一个带有3个基本案例和一个递归子句的递归谓词。

div3([], [], [], []).
div3([X], [X], [], []).
div3([X,Y], [X], [Y], []).
div3([X,Y,Z|Tail], [X|XTail], [Y|YTail], [Z|ZTail]):-
  div3(Tail, XTail, YTail, ZTail).

答案 1 :(得分:0)

在递归谓词div3 / 5的代码中没有结束情况,前3个子句仅适用于div3 / 3调用(这就是为什么调用div3([4,2,42],X,Y) ,Z)成功)

另外,你用一个元素而不是一个列表来调用append / 3,所以它失败了(除非你有一个列表但是在这种情况下,它不是你想要的)

我建议切换到更“声明”的方法,可能使用get_N_elements(List,List_N,Rest)之类的谓词来避免代码重复

答案 2 :(得分:0)

如果维护源顺序无关紧要,以下就足够了。

divide( []        , []     , []     , []     ) .  % we're done when the source list is exhausted, OR ...
divide( [X]       , [X]    , []     , []     ) .  % - it's only got 1 element, OR ...
divide( [X,Y]     , [X]    , [Y]    , []     ) .  % - it's only got 2 elements
divide( [X,Y,Z|T] , [X|Xs] , [Y|Ys] , [Z|Zs] ) :- % otherwise, split three elements amount the result lists and
  divide(T,Xs,Ys,Zs)                              % - recurse down.
  .                                               %

上面的代码对列表进行分区

[a,b,c,d,e,f,g]

  • [a,d,g]
  • [b,e]
  • [c,f]

如果您希望维护订单,这将有效,描述什么构成正确的解决方案(例如,长度列表尽可能相等)并让append/3找到正确的解决方案:

divide( L , X , Y , Z ) :-
  append(X,T,L)               , % split X off as a prefix of the source list L
  append(Y,Z,T)               , % divide the remainder (T) into a prefix Y and suffix Z
  length(X,X1)                , % compute the length of X
  length(Y,Y1)                , % compute the length of Y
  length(Z,Z1)                , % compute the length of Z
  min_max([X1,Y1,Z1],Min,Max) , % determine the shortest and longest such length
  Max - Min =< 1 ,              % and ensure that the delta is 1 or less
  .

min_max([],0,0) .
min_max([H|T],Min,Max) :-
  min_max(T,H,H,Min,Max)
  .

min_max([],Min,Max,Min,Max) .
min_max([H|T], T1 , T2 , Min , Max ) :-
  ( H < T1 -> T3 = H ; T3 = T1 ) ,
  ( H > T2 -> T4 = H ; T4 = T2 ) ,
  min_max(T,T3,T4,Min,Max)
  .

以上基本上说

  

将列表L分为3个子列表X,Y和Z,使得长度之间的差值为   每个子列表不超过1个。

在这种情况下,您应该看到列表

[a,b,c,d,e,f,g]

分为

  • [a,b]
  • [c,d]
  • [e,f,g]

应该注意到这是非确定性的,回溯将找到所有可能的解决方案。