Prolog - 将列表分成n个元素的部分

4

我有一个预测函数,可以获取前N个元素:

nfirst(N, _, Lnew) :- N =< 0, Lnew = [].
nfirst(_, [], []).
nfirst(N, [X|Y], [X|Y1]) :- N1 is N - 1, nfirst(N1, Y, Y1).

它可以工作:

% nfirst(3,[1,2,3,4,5,6],X).
% X = [1, 2, 3]

我需要一个预测来分割以下列表:

% divide([a,b,c,d,e,f,g,h],[3,2,1,2],X).
% X = [[a,b,c],[d,e],[f],[g,h]]

最好的方法是使用nfirst。

1
divide([a,b,c,d,e], [3, 4], X)应该做什么?你必须使用你定义的nfirst/3吗?这不是最好的方法。比你定义的nfirst/3更好的谓词是nfirst(N, List, First, Rest),其中First是前N个元素,Rest是剩余的元素。 - lurker
这个有帮助吗?split_at(N, List, [H|[T]]) :- append(H, T, List), length(H, N). - Daniel Lyons
1个回答

5
非常相似的问题,类似于我在这里回答的。同样,诀窍是使用append/3加上length/2来“咬掉”列表的一部分,就像我上面的评论所说的一样:
split_at(N, List, [H|[T]]) :- append(H, T, List), length(H, N).

如果你运行它,你会看到下面这个:
?- split_at(4, [1,2,3,4,5,6,7,8], X).
X = [[1, 2, 3, 4], [5, 6, 7, 8]] ;

这就是您程序的骨架,现在您只需要围绕它进行通常的递归处理即可。首先是基本情况,即如果我不在列表中,那么我应该不在分割位置之内,因此也不在结果中:

divide([], [], []).

请注意,这样明确的基本情况使您的程序比像 divide([], _, _) 这样的东西更正确,因为如果您获得了过多的拆分位置,则会导致失败。
现在,递归情况并不困难,但是由于split_at/3将两个东西放在一个列表中(可能是一个糟糕的选择,您可以通过创建split_at/4来进行改进),因此您必须将它们取出,并且在这里掩盖了逻辑,同时使API更好(在我看来)。
divide(List, [Split|Splits], [Chunk|Rest]) :-
    split_at(Split, List, [Chunk, Remainder]),
    divide(Remainder, Splits, Rest).

这应该很简单:我们只是使用 Split 函数将位置分割出来,用它来分割列表,并在剩下的部分上重复处理。看起来它能像你预期的那样工作:

?- divide([a,b,c,d,e,f,g,h],[3,2,1,2],X).
X = [[a, b, c], [d, e], [f], [g, h]] ;
false.

希望这能帮到你!与其他答案相比,它可能更加清晰易懂。

我再次需要帮助。 - user

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接