Как я могу разделить список [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]] (не имеет значения).

Есть идеи по этому поводу или предложения, как решить эту проблему?

Спасибо.

-1
Alex Gel 1 Май 2019 в 07:38

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).

Это просто рекурсивное удаление одного отсортированного префикса за раз.

0
Daniel Lyons 1 Май 2019 в 06:56

Мне показалось, что это проблема 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"?

2
Isabelle Newbie 1 Май 2019 в 09:49

Довольно сложно, но это можно сделать с помощью 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

.

1
joel76 1 Май 2019 в 22:12