Сортировать список по второму атому в функторе
У меня есть этот список в прологе:
List = [functor(a,1),functor(n,7),functor(l,9),functor(k,0)]
и я хочу отсортировать его по номеру в функторе вместо буквы. Я имею в виду, если я делаю sort(L,L1)
я получу
L1 = [functor(a,1),functor(k,0),functor(l,9),functor(n,7)]
но я хочу, чтобы это было похоже
L1 = [functor(k,0),functor(a,1),functor(n,7),functor(l,9)]
есть ли способ сделать это (а не просто воссоздать его и поставить номер первым)
1 ответ
Чтобы "воссоздать" список (вид):
?- List = [functor(a,1),functor(n,7),functor(l,9),functor(k,0)],
map_list_to_pairs(arg(2), List, Pairs),
keysort(Pairs, Sorted_pairs),
pairs_values(Sorted_pairs, Sorted).
Pairs = [1-functor(a, 1), 7-functor(n, 7), 9-functor(l, 9), 0-functor(k, 0)],
Sorted = [functor(k, 0), functor(a, 1), functor(n, 7), functor(l, 9)].
Смотрите здесь. Это, вероятно, самый "идиоматический" способ сделать это, учитывая, что ваша реализация имеет library(pairs)
, Если этого не произойдет, то используемые здесь предикаты почти тривиальны: см. Исходный код библиотеки.
Если вы используете predsort
см. вопрос, связанный также в комментарии. Вы можете определить предикат помощника, например:
compare_2(Order, A, B) :-
arg(2, A, A2),
arg(2, B, B2),
compare(Order, A2-A, B2-B).
(исправление @false)
Это будет сортировать по второму аргументу первым, а если они равны, по всему члену.
?- List = [f(a,1),f(n,0),f(l,9),f(k,0)],
predsort(compare_2, List, Sorted).
List = [f(a, 1), f(n, 0), f(l, 9), f(k, 0)],
Sorted = [f(k, 0), f(n, 0), f(a, 1), f(l, 9)].
Другими словами, это не "стабильный вид". Так что на самом деле, лучше использовать keysort
подход.
Обратите внимание, что эта версия compare_2
будет по-прежнему удалять все, кроме первого появления идентичных терминов, потому что это то, что predsort
делает:
?- List = [f(a,1),f(n,0),f(l,9),f(n,0)],
predsort(compare_2, List, Sorted).
List = [f(a, 1), f(n, 0), f(l, 9), f(n, 0)],
Sorted = [f(n, 0), f(a, 1), f(l, 9)].
Вы можете "обмануть" его, определив, что два элемента, которые эквивалентны (но не равны), уже отсортированы:
compare_2_stable(Order, A, B) :-
arg(2, A, A2),
arg(2, B, B2),
compare(Order0, A2, B2),
( Order0 == (=)
-> Order = (<)
; Order = Order0
).
?- List = [f(a,1),f(n,0),f(l,9),f(k,0)],
predsort(compare_2_stable, List, Sorted).
List = [f(a, 1), f(n, 0), f(l, 9), f(k, 0)],
Sorted = [f(n, 0), f(k, 0), f(a, 1), f(l, 9)].
?- List = [f(a,1),f(n,0),f(l,9),f(n,0)],
predsort(compare_2_stable, List, Sorted).
List = [f(a, 1), f(n, 0), f(l, 9), f(n, 0)],
Sorted = [f(n, 0), f(n, 0), f(a, 1), f(l, 9)].
Обратите внимание, что я точно не знаю, сохранит ли это исходный порядок эквивалентных элементов (элементов с "равными" вторыми членами). Это делает это в данный момент, для этого примера. Это будет иметь место только в том случае, если predsort
сохраняет элементы в их первоначальном порядке, когда передает их в предикат сравнения, и это может зависеть от реализации (??).
РЕДАКТИРОВАТЬ: посмотрев на исходный код predsort/3
в SWI-Prolog, да, в SWI-Prolog, при текущей реализации последнее решение даст точно такое же решение, как и при использовании keysort/2
, Тем не менее, вы должны предпочесть keysort/2
если у вас нет практической причины не делать этого, и вы готовы пожертвовать переносимостью.