非常相似的问题,类似于我在
这里回答的。同样,诀窍是使用
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.
希望这能帮到你!与其他答案相比,它可能更加清晰易懂。
divide([a,b,c,d,e], [3, 4], X)
应该做什么?你必须使用你定义的nfirst/3
吗?这不是最好的方法。比你定义的nfirst/3
更好的谓词是nfirst(N, List, First, Rest)
,其中First
是前N
个元素,Rest
是剩余的元素。 - lurkersplit_at(N, List, [H|[T]]) :- append(H, T, List), length(H, N).
- Daniel Lyons