我认为您不会找到既完全通用又效率最高的解决方案。这是 N = 3 的简单解决方案:
edges(Edges) :-
Goal = edge(_A, _B, _C),
findall(Goal, Goal, Edges).
edges_abcs_([], [], [], []).
edges_abcs_([edge(A,B,C)|Edges], [A|As], [B|Bs], [C|Cs]) :-
edges_abcs_(Edges, As, Bs, Cs).
edges_abcs([As, Bs, Cs]) :-
edges(Edges),
edges_abcs_(Edges, As, Bs, Cs).
添加 100,000 个额外的 edge/3 事实后,执行如下:
?- time(edges_abcs(M)).
% 200,021 inferences, 0.063 CPU in 0.065 seconds (97% CPU, 3176913 Lips)
M = [[a, b, c, d, 1, 2, 3, 4|...], [1, 2, 3, 4, 1, 2, 3|...], [10, 20, 30, 40, 1, 2|...]].
为了比较,这里是对问题实施的测量:
?- time(edgeMatrix_orig(M)).
% 300,043 inferences, 0.061 CPU in 0.061 seconds (100% CPU, 4896149 Lips)
M = [[a, b, c, d, 1, 2, 3, 4|...], [1, 2, 3, 4, 1, 2, 3|...], [10, 20, 30, 40, 1, 2|...]].
下面是 Willem 基于transpose/2 的更通用的解决方案:
?- time(edgeMatrix_transpose(M)).
% 700,051 inferences, 0.098 CPU in 0.098 seconds (100% CPU, 7142196 Lips)
M = [[a, b, c, d, 1, 2, 3, 4|...], [1, 2, 3, 4, 1, 2, 3|...], [10, 20, 30, 40, 1, 2|...]].
因此,就推理数量而言,我的解决方案似乎是最好的:findall/3 的推理次数为 100,000 次,遍历列表的推理次数为 100,000 次。该问题的解决方案对每个 findall/3 有 100,000 个推论,但仅此而已。但是,它稍微快一些,因为它更节省内存:分配的所有内容最终都在最终解决方案中,而我的程序分配了 100,000 个edge/3 术语的列表,然后必须对其进行垃圾收集。 (在 SWI-Prolog 中,如果您打开分析器和/或 GC 跟踪,您可以看到垃圾回收。)
如果我真的需要它尽可能快并且可以推广到许多不同的 N 值,我会编写一个扩展为类似的宏问题中的解决方案。
编辑:如果取消“惯用”要求,我将求助于将edge 数据库作为列表存储在 SWI-Prolog 全局变量中。在这种情况下,我的单通道实现可以在没有findall/3 开销的情况下工作,并且不会产生中间垃圾。