Я не думаю, что вы найдете решение, которое является полностью общим и максимально эффективным. Вот простое решение для 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|...]].
А вот более общее решение, основанное на 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|...]].
Таким образом, мое решение кажется лучшим с точки зрения количества выводов: 100 000 выводов для findall/3
и 100 000 выводов для перемещения по списку.Решение вопроса состоит из 100 000 выводов для каждого findall/3
, но не более того. Это, однако, немного быстрее, потому что это более эффективно с памятью: все, что выделяется, попадает в окончательное решение, тогда как моя программа выделяет список из 100 000 edge/3
условий, которые затем должны быть собраны в мусор. (В SWI-Prolog, вы можете увидеть коллекции мусора, если вы включите профайлер и/или отслеживание GC.)
Если я действительно нужен, чтобы это было как можно быстрее и быть обобщенными многие разные значения N, я бы написал макрос, который расширяется до чего-то вроде решения в вопросе.
Редактировать: Если «idiomatic» требование поднято, я прибегнув к хранению базы данных edge
в виде списка в глобальной переменной SWI-Prolog. В этом случае моя однопроходная реализация будет работать без накладных расходов findall/3
и без промежуточного мусора.
Это было очень изобретательно ... Мне это нравится ;-) Хотя это может иметь ту же самую проблему с производительностью, о которой я упоминал из-за 'transpose/2'. –
@HugoSerenoFerreira Вы можете посмотреть определение 'transpose/2', но я думаю, что это примерно так же эффективно, как и список списков в виде матричного представления. В общем, нет простого, общего способа справиться с матрицами в чистом Prolog (насколько я знаю). Возможно, вы можете избежать использования списка списков для начала, но ваш вопрос не дает достаточно подробностей. –