【问题标题】:Prolog - How to make a list of lists with a certain length from a flat listProlog - 如何从平面列表中制作具有一定长度的列表列表
【发布时间】:2018-03-19 02:50:15
【问题描述】:

例如:

createlistoflists([1,2,3,4,5,6,7,8,9], NewLists)
NewLists = [[1,2,3], [4,5,6], [7,8,9].

所以基本上我的第一个参数是一个列表,我的第二个参数是一个由具有适当长度(适当长度为 3)的列表组成的新列表。我的第一个想法是使用某种附加功能。但是我真的不知道该怎么做,有什么想法吗?

提前致谢

【问题讨论】:

  • 我在您的示例中看不到您如何指定“适当的长度”。您是否假设它总是(硬编码为)3?另外,如果你做createlistoflists([1,2,3,4,5,6,7], NewLists)怎么办?你期待什么结果?
  • 是的,正确的长度被硬编码为 3,这是我在下面写的结果。 NewLists 应该输出一个列表,里面有 3 个长度为 3 的列表。
  • 是的,但是您对createlistoflists([1,2,3,4,5,6,7], NewLists) 的期望是什么?你想让它失败吗?

标签: list prolog chunking


【解决方案1】:

如果您不介意使用 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]).

【讨论】:

  • 重试时出现分歧。
  • @WillNess 感谢您了解这一点。我发誓我已经对此进行了测试,但是当我刚刚尝试时,确实,它无法在重试时终止。
  • 很高兴为您提供帮助。我非常好奇如何/是否可以限制纯粹
  • 不错的老式手工制作not_longer_than!也许如果你完全放弃maplist,那么最后一组可能较短的列表也可以处理。
  • @WillNess 是的,也许我不合理地坚持使用 mapllist ... 最初这似乎是一种时髦的方式,与 append/2 一起走向一个简单的解决方案。但它确实开始变得更加复杂。所以,我发布了一个看起来并不那么糟糕的直接方法。\
猜你喜欢
  • 2021-11-28
  • 2022-01-09
相关资源
最近更新 更多