如果您不介意使用 Prolog 为您提供的优质设施,有一个简单的方法;
list_length(Size, List) :- length(List, Size).
split_list(List, SubSize, SubLists) :-
maplist(list_length(SubSize), SubLists),
append(SubLists, List).
你可以这样查询:
?- split_list([1,2,3,4,5,6,7,8,9], 3, L).
L = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
如果List 的实例化方式使其长度不是SubSize 的倍数,它将失败。
正如 Will Ness 在 cmets 中指出的那样,上述简单的解决方案有一个缺陷:
maplist(list_length(SubSize), SubList) 将继续查询并找到越来越长的子列表集,不受约束。因此,在重试时,上述查询不会终止。
诱惑是使用这样的剪辑:
split_list(List, SubSize, SubLists) :-
maplist(list_length(SubSize), SubLists), !,
append(SubLists, List).
这里的剪辑假设您只想得到一个答案,就好像您正在编写一个命令式函数一样。
更好的方法是尝试以合乎逻辑的方式将SubList 参数限制为maplist。一个简单的方法是确保SubList 的长度不超过List 的长度,因为从逻辑上讲,它永远不应该更大。添加此约束:
list_length(Size, List) :- length(List, Size).
not_longer_than([], []).
not_longer_than([], [_|_]).
not_longer_than([_|X], [_|Y]) :-
not_longer_than(X, Y).
split_list(List, SubSize, SubLists) :-
not_longer_than(SubLists, List),
maplist(list_length(SubSize), SubLists),
append(SubLists, List).
然后查询终止而不会失去解决方案的一般性:
?- split_list([1,2,3,4,5,6,7,8,9], 3, L).
L = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] ;
false.
?-
在not_longer_than/2 的实现中可以更精确,并让它使用SubSize 作为倍数。这样会更有效率,但不需要终止。
not_longer_than_multiple(L1, Mult, L2) :-
not_longer_than_multiple(L1, Mult, Mult, L2).
not_longer_than_multiple([], _, _, []).
not_longer_than_multiple([], _, _, [_|_]).
not_longer_than_multiple([_|Xs], Mult, 1, [_|Ys]) :-
not_longer_than_multiple(Xs, Mult, Mult, Ys).
not_longer_than_multiple(Xs, Mult, C, [_|Ys]) :-
C #> 1,
C1 #= C - 1,
not_longer_than_multiple(Xs, Mult, C1, Ys).
或者类似的东西......
但是,如果我们要通过所有这些废话来掩盖这种使用
maplist 的罪过,那么正面解决问题可能是最干净的解决方案:
split_list(List, SubSize, SubLists) :-
split_list(List, SubSize, SubSize, SubLists).
split_list([], _, _, []).
split_list([X|Xs], SubList, 1, [[X]|S]) :-
split_list(Xs, SubList, SubList, S).
split_list([X|Xs], SubSize, C, [[X|T]|S]) :-
C #> 1,
C1 #= C - 1,
split_list(Xs, SubSize, C1, [T|S]).