Как я могу разделить список [5,4,2,8,3,1,6,9,5] на список подсписков, которые будут разбиты на значения, нарушающие последовательность?
Например, список [5,4,2,8,3,1,6,9,5]
должен давать список подсписков, таких как [5], [4], [2,8], [3], [1,6,9], [5] or [[5], [4], [2,8], [3], [1,6,9], [5]]
(не имеет значения).
Есть идеи по этому поводу или предложения, как решить эту проблему?
Спасибо.
3 ответа
Вы только что попросили стратегию, но я действительно не смог придумать хорошую. Я надеюсь, что кто-то другой придет с лучшим подходом, чем любой из моих.
Я действительно недоволен своим решением, потому что мне кажется, что эта простая проблема заслуживает простого решения, а мое решение не очень простое. На самом деле, я чувствую, что-то вроде этого должно работать:
sorted_sublists([], []).
sorted_sublists(L, [Prefix|Remaining]) :-
append(Prefix, Suffix, L),
sort(Prefix, Prefix),
sorted_sublists(Suffix, Remaining).
Это кажется мне достаточно декларативным: дайте мне префикс L, если он уже отсортирован, поместите его в список результатов и вернитесь к тому, что осталось. Однако, это не работает, потому что Префикс может быть пустым списком, но если вы исправите это так:
sorted_sublists([], []).
sorted_sublists(L, [Prefix|Remaining]) :-
append(Prefix, Suffix, L),
Prefix=[_|_],
sort(Prefix, Prefix),
sorted_sublists(Suffix, Remaining).
Это все еще не работает, потому что вы получаете множество решений, последний из которых именно тот, который вы действительно хотите:
[debug] ?- sorted_sublists([1,2,3,1,2,1,1], Sublists).
Sublists = [[1], [2], [3], [1], [2], [1], [1]] ;
Sublists = [[1], [2], [3], [1, 2], [1], [1]] ;
Sublists = [[1], [2, 3], [1], [2], [1], [1]] ;
Sublists = [[1], [2, 3], [1, 2], [1], [1]] ;
Sublists = [[1, 2], [3], [1], [2], [1], [1]] ;
Sublists = [[1, 2], [3], [1, 2], [1], [1]] ;
Sublists = [[1, 2, 3], [1], [2], [1], [1]] ;
Sublists = [[1, 2, 3], [1, 2], [1], [1]] ;
false.
Тем не менее, это похоже на движение в правильном направлении. Что если бы у нас был предикат, который мог бы отделить первый отсортированный префикс? Если бы у нас было это, мы могли бы пропустить append/3
и ошибочные решения. Итак, давайте сосредоточимся на написании этого предиката. Я придумал это:
sorted_prefix([Last], [Last], []).
sorted_prefix([X,Y|Ys], Prefix, Suffix) :-
(X < Y ->
sorted_prefix([Y|Ys], Prefix0, Suffix),
Prefix = [X|Prefix0]
;
Prefix = [X], Suffix = [Y|Ys]
).
Таким образом, базовый случай состоит в том, что у нас есть только один элемент в нашем списке. Это отсортированный префикс.
Индуктивный случай сложнее. Идея состоит в том, что если первые два элемента в порядке, я хочу повторить второй элемент плюс оставшийся список, и я хочу добавить свой результат к этому результату. Другими словами, если отсортированный префикс L равен R и X меньше, чем первый элемент L, то отсортированный префикс [X | L] равен [X | R]. Если это не так, мы попадаем в другую ситуацию: если X больше, чем первый элемент L, отсортированный префикс [X | L] просто [X]. В этом случае мы также должны определить суффикс, который будет просто L.
Окончательный sorted_sublists/2
становится немного проще:
sorted_sublists([], []).
sorted_sublists(L, [Prefix|Remaining]) :-
sorted_prefix(L, Prefix, Suffix),
sorted_sublists(Suffix, Remaining).
Это просто рекурсивное удаление одного отсортированного префикса за раз.
Мне показалось, что это проблема DCG, поэтому вот одно решение DCG:
ascending([X|Xs]) -->
[X],
ascending(X, Xs).
ascending(X, [Y|Xs]) -->
[Y],
{ X =< Y },
{ ! },
ascending(Y, Xs).
ascending(_X, []) -->
[].
Это можно использовать с phrase/3
для получения как отсортированного префикса, так и оставшихся элементов:
?- phrase(ascending(Asc), [1,2,3,4,5], Rest).
Asc = [1, 2, 3, 4, 5],
Rest = [].
?- phrase(ascending(Asc), [1,2,3,4,5,2,3,4,5], Rest).
Asc = [1, 2, 3, 4, 5],
Rest = [2, 3, 4, 5].
?- phrase(ascending(Asc), [1,2,3,4,5,2,3,4,5], Rest), phrase(ascending(Asc2), Rest, Final).
Asc = [1, 2, 3, 4, 5],
Rest = Asc2, Asc2 = [2, 3, 4, 5],
Final = [].
Основным предикатом является просто:
sorted_sublists([], []).
sorted_sublists(List, [Prefix|Remaining]) :-
phrase(ascending(Prefix), List, Rest),
sorted_sublists(Rest, Remaining).
Тем не менее, сокращение ascending//2
несколько уродливо. Отрицание в DCG немного утомительно, но с ограничениями его можно заставить работать:
:- use_module(library(clpfd)).
ascending(X, [Y|Xs]) -->
{ X #=< Y },
[Y],
ascending(Y, Xs).
ascending(X, []) -->
{ X #=< Y },
\+ [Y].
Это довольно мило, я думаю. Есть ли способ сделать что-то подобное, но не обязательно с ограничениями? По сути, способ записи в DCG "соответствует пустому списку или непустой список, который не удовлетворяет некоторому предикату P
"?
Довольно сложно, но это можно сделать с помощью DCG:
sub([A,B|T]) --> [[A]], {A > B},sub([B|T]); {A =< B, phrase(sub_1([A,B|T], S), R, [])}, [R], sub(S).
sub([A]) --> [[A]].
sub([]) --> [].
sub_1([A,B|T], S) --> [A], {A =< B}, sub_1([B|T], S);[A], {A > B, S = [B|T]}.
sub_1([A], []) --> [A].
Результат:
?- phrase(sub([5,4,2,8,3,1,6,9,5] ), A, []).
A = [[5], [4], [2, 8], [3], [1, 6, 9], [5]] ;
false
.
Похожие вопросы
Связанные вопросы
Новые вопросы
prolog
Не используйте этот тег, как Пролог и Эпилог. Пролог является наиболее часто используемым языком логического программирования. Он поддерживает недетерминированное программирование посредством хронологического обратного отслеживания и сопоставления с образцом посредством унификации.