Плато списка пролога

Только что познакомился с прологом, пытаясь выполнить несколько простых упражнений, но я как-то застрял на этом. Я пытаюсь написать программу, которая выводит все подсписки входного списка, где каждый подсписок имеет длину> 1, и его нельзя расширить до большего подсписка. Он также выведет начальную позицию в списке подсписка. Таким образом, образец вывода будет

| ?- plateau([a,a,b,2,2,2,a+1,a+1,s(1,2)], I, Len).
    I = 1,
    Len = 2 ? ;
    I = 4,
    Len = 3 ? ;
    I = 7,
    Len = 2 ? ;
    no

Я все еще довольно сбит с толку всей этой декларативностью, и у меня много проблем с переключением из императивного режима. Я думаю, что хочу, чтобы моя программа делала что-то вроде

program([H|T],I,L):-
    T = [H1|T1] %split the tail
    ([H] = [H1] -> Count is Count+1, program(T,I,Count) 
     %if First element = second element, recurse with new values
    ; length(T,Spot), 
      %get the spot where you are in the list, so we know where sublist starts
      program(T,Spot,L) %run again, from tail, since sublist didn't have another  element?
program([],I,L). %terminate when entire list has been run through?

Так что это не работает, насколько я могу судить по нескольким причинам. Я не сбрасываю «счетчик», так что, может быть, он суммирует значения всех подсписков вместе? Есть ли способ обойти это? Мой базовый вариант также может быть не тем, что я хочу - я просто не уверен, каким он должен быть на самом деле? Я, вероятно, упускаю и другие вещи... любая помощь очень ценится! :) Спасибо!


person user1257768    schedule 13.03.2012    source источник
comment
@false, почему тег DCG? Да ладно, ОП говорит, что просто хочу изучить самые основы Пролога! Пожалуйста, рассмотрите возможность удаления этого тега.   -  person Will Ness    schedule 17.03.2012
comment
@Will Ness: Если вы действительно хотите обсудить такие вопросы, используйте мета.   -  person false    schedule 17.03.2012


Ответы (5)


Здесь довольно много сложных ответов. Рассмотрим этот, который не использует DCG или многие встроенные модули (возможно, проще для новичка):

plateau([X|Xs], I, L) :-
    plateau(Xs, 1-1-X, I, L).

plateau([X1|Xs], I0-L0-X0, I, L) :-
    X0 == X1, !,
    NL0 is L0 + 1,
    plateau(Xs, I0-NL0-X0, I, L).

plateau(_, I-L-_, I, L) :-
    L > 1.

plateau([X|Xs], I0-L0-_, I, L) :-
    NI is I0 + L0,
    plateau(Xs, NI-1-X, I, L).

Первое предложение устанавливает вызов, который накапливает кортеж (index)-(length)-(sublist element) в качестве терма.

Следующее предложение увеличивает length, если следующий список element такой же (обратите внимание, что индекс не изменяется).

Третье предложение вызывается только в том случае, если второе предложение дало сбой при проверке того, был ли нарушен запуск элемента подсписка (из-за сокращения !), и возвращает решение, если length выполнения было > 1.

Последнее предложение позволяет Прологу вернуться и перезапустить поиск с последнего прогона.

EDIT: решение gusbro на самом деле очень близко к этому... +1.

person Community    schedule 15.03.2012
comment
+1: ваше решение - единственная возможная альтернативная интерпретация: вы обрабатываете список как есть, как если бы все переменные в списке были количественно определены. Вы используете разрез, но с (==)/2 в качестве охранника. - person false; 18.03.2012

Ваша программа объединяет множество различных вопросов в один предикат. Попробуем немного их разделить. Кроме того, я предполагаю, что вы ищете максимальный подсписок из двух или более элементов, содержащих идентичные элементы.

Давайте начнем с приблизительного того, что вы хотите: Идентификация подсписков. Не беспокойтесь, что это слишком широко, мы уточним это позже. Я буду использовать DCG для этой цели. Нетерминал seq//1 описывает произвольную последовательность.

seq([]) --> [].
seq([E|Es]) --> [E], seq(Es).

Это крайне полезный нетерминал!

?- phrase((seq(Prefix),seq(Sublist),seq(Postfix)),
      [a,a,b,2,2,2,a+1,a+1,s(1,2)]).
Prefix = Sublist, Sublist = [],
Postfix = [a,a,b,2,2,2,a+1,a+1,s(1,2)] ;
Prefix = [],
Sublist = "a",
Postfix = [a,b,2,2,2,a+1,a+1,s(1,2)] ...

Оба ответа не ожидаются, нам нужны только подсписки длиной 2 или более, поэтому мы должны немного ограничить это определение. Скажем, потребовав, чтобы Sublist содержал как минимум два элемента. Это Sublist = [_,_|_].

?- Sublist = [_,_|_],
   phrase((seq(Prefix),seq(Sublist),seq(Postfix)),
      [a,a,b,2,2,2,a+1,a+1,s(1,2)]).
Sublist = "aa",
Prefix = [],
Postfix = [b,2,2,2,a+1,a+1,s(1,2)] ;
Sublist = "aab",
Prefix = [],
Postfix = [2,2,2,a+1,a+1,s(1,2)] ...

Первый ответ показывает подсписок, который мы ищем. Но второе по-прежнему неверно: все элементы подсписка должны быть равны. Самый простой способ - использовать maplist/2:

?- maplist(=(_),Es).
Es = [] ;
Es = [_G221] ;
Es = [_G221,_G221] ;
Es = [_G221,_G221,_G221] 

Есть несколько мест, где мы могли бы указать это требование. Поставлю как можно раньше:

?- Sublist = [_,_|_],
        phrase(( seq(Prefix),
                 seq(Sublist),{maplist(=(_),Sublist)},
                 seq(Postfix)),
           [a,a,b,2,2,2,a+1,a+1,s(1,2)]).
Sublist = "aa",
Prefix = [],
Postfix = [b,2,2,2,a+1,a+1,s(1,2)] ;
Sublist = [2,2],
Prefix = "aab",
Postfix = [2,a+1,a+1,s(1,2)] ;
Sublist = [2,2,2],
Prefix = "aab",
Postfix = [a+1,a+1,s(1,2)] ;
Sublist = [2,2],
Prefix = [a,a,b,2],
Postfix = [a+1,a+1,s(1,2)] ;
Sublist = [a+1,a+1],
Prefix = [a,a,b,2,2,2],
Postfix = [s(1,2)] ;
false.

Итак, теперь все подсписки содержат одинаковые элементы. Увы, мы получаем как [2,2], так и [2,2,2] в качестве подсписка. Нам нужен только максимальный подсписок. Итак, как мы можем описать, что такое максимальный подсписок?

Один из способов — посмотреть перед нашим подсписком: там не должно быть одного и того же элемента нашего подсписка. Таким образом, либо впереди ничего (эпсилон), либо последовательность, которая заканчивается элементом, отличным от нашего.

difel(_E,[]).
difel(E, Es) :- dif(E,F), phrase((seq(_), [F]), Es).
?- Sublist = [_,_|_],
   phrase(( seq(Prefix),{difel(E,Prefix)},
            seq(Sublist),{maplist(=(E),Sublist)},
            seq(Postfix)),
      [a,a,b,2,2,2,a+1,a+1,s(1,2)]).
Sublist = "aa",
Prefix = [],
E = a,
Postfix = [b,2,2,2,a+1,a+1,s(1,2)] ;
Sublist = [2,2],
Prefix = "aab",
E = 2,
Postfix = [2,a+1,a+1,s(1,2)] ;
Sublist = [2,2,2],
Prefix = "aab",
E = 2,
Postfix = [a+1,a+1,s(1,2)] ;
Sublist = [a+1,a+1],
Prefix = [a,a,b,2,2,2],
E = a+1,
Postfix = [s(1,2)] ;
false.

Одним неверным ответом меньше. В конце еще один скрывается.

?- Sublist = [_,_|_],
   phrase(( seq(Prefix),{difel(E,Prefix)},
            seq(Sublist),{maplist(=(E),Sublist)},
            ( [] | [F],{dif(E,F)},seq(_) ) ),
      [a,a,b,2,2,2,a+1,a+1,s(1,2)]).
Sublist = "aa",
Prefix = [],
E = a,
F = b ;
Sublist = [2,2,2],
Prefix = "aab",
E = 2,
F = a+1 ;
Sublist = [a+1,a+1],
Prefix = [a,a,b,2,2,2],
E = a+1,
F = s(1,2) ;
false.

Это не совсем то, что вы хотели: вы просто хотели длины. Для этого добавьте length([_|Prefix],I), length(Sublist,Len).

Итак, вот окончательное определение:

plateau(Xs, I, Len) :-
   Sublist = [_,_|_],
   phrase(( seq(Prefix),{difel(E,Prefix)},
            seq(Sublist),{maplist(=(E),Sublist)},
            ( [] | [F],{dif(E,F)},seq(_) ) ),
      Xs),
   length([_|Prefix],I),
   length(Sublist,Len).
person false    schedule 13.03.2012
comment
Спасибо! Мне нужно будет просмотреть это некоторое время и убедиться, что я все понимаю - все еще просто выясняя основы! - но я очень ценю помощь :) - person user1257768; 13.03.2012
comment
@ user1257768: Как новичок, сначала придерживайтесь списков и DCG. Не делайте слишком много с (is)/2, (>)/2 или даже с !/0. Вам нужно слишком много знать о фактическом выполнении, чтобы писать правильные программы. - person false; 13.03.2012
comment
Очень сбивает с толку новичка. Они только что познакомились с прологом, и вы бросаете им это? - person Will Ness; 16.03.2012
comment
@ user1257768 как новичок, в первую очередь придерживайтесь списков, арифметики и вырезания. Я рекомендую Искусство Пролога. Придерживайтесь порядка глав. Когда вы доберетесь до списков различий, помните, что речь идет о явном сохранении указателя конца списка. Затем перейдите к DCG. - person Will Ness; 16.03.2012
comment
@Will Ness: Книга, которую вы упомянули, содержит главу 8: арифметика, главу 11: сокращения и отрицание. В предыдущих главах он не используется. И ни указатель конца списка, ни списки различий не являются понятиями, которые вам нужны для понимания DCG. - person false; 16.03.2012
comment
YMMV. :) Для меня DCG — это продвинутая концепция над Прологом. Точно так же, как ограничения, такие как dif/2. - person Will Ness; 16.03.2012
comment
@Will Ness: давайте продолжим ответ gusbro! Это весьма показательно для многих вещей, которые обычно приходится контролировать, когда в программе присутствуют сокращения и тому подобное. - person false; 17.03.2012
comment
@false, а не что, ограничения?? - person Will Ness; 17.03.2012
comment
@Will Ness: В отличие от программ, которые не используют сокращения и тому подобное, например, if-then-else с условием if, представляющим собой унификацию, как в программе gusbro. - person false; 17.03.2012

Я пытался использовать встроенный nth1/3, но у меня было больше проблем с его работой... во всяком случае, вот еще одна реализация:

plateau(L, I, Len) :-
    plateau(L, 1, I, Len).
plateau(L, P, I, Len) :-
    nth1(P, L, E),
    skipseq(P, L, E, J),
    (   J > P,
        Len is J - P + 1,
        I is P
    ;   Q is J + 1,
        plateau(L, Q, I, Len)
    ).

% get the index J of last element E (after I)
skipseq(I, L, E, J) :-
    T is I + 1,
    nth1(T, L, E),
    !, skipseq(T, L, E, J).
skipseq(I, _, _, I).

контрольная работа:

[debug]  ?- plateau([a,x,x,x,u,u,h,w],I,N).
I = 2,
N = 3 ;
I = 5,
N = 2 ;
false.
person CapelliC    schedule 13.03.2012

Вы можете сделать что-то вроде этого:

plateau([Item|Tail], I, Len):-
  plateau(Tail, 1, Item, 1, I, Len).

plateau(List, From, NItem, Len, From, Len):-
  (List=[Item|_] -> (Item\=NItem;var(Item)); List = []),
  Len > 1.
plateau([Item|Tail], IFrom, Item, ILen, From, Len):-
  MLen is ILen + 1,
  plateau(Tail, IFrom, Item, MLen, From, Len).
plateau([Item|Tail], IFrom, OItem, ILen, From, Len):-
  OItem \= Item,
  NFrom is IFrom + ILen,
  plateau(Tail, NFrom, Item, 1, From, Len).

Первый пункт плато/6 касается завершения подсписка. Либо элемент отличается от того, который вы ищете, либо вы достигли конца списка. В этом случае мы продолжаем, только если текущая длина больше единицы.

Второе предложение касается шага рекурсии для случая, когда мы все еще сопоставляем элемент в списке. Он просто добавляет единицу к счетчику текущего подсписка и выполняет рекурсию.

Последнее предложение касается случая обнаружения нового (другого) элемента в списке и просто сбрасывает счетчики и выполняет рекурсию.

person gusbro    schedule 13.03.2012
comment
X = b, plateau([a,a,X], 1, 2). удается, но его обобщение plateau([a,a,X], 1, 2). терпит неудачу. - person false; 13.03.2012
comment
Это потому, что он инстанцирует свободную переменную с a, таким образом, он дает I=1, Len=3. Алгоритм, который я написал, является жадным и просто объединяет переменные с предыдущим элементом в списке. - person gusbro; 13.03.2012
comment
Я не уверен, понимаю ли я проблему, на которую указывает false? Наверно, выше головы... :) - person user1257768; 13.03.2012
comment
Ложь говорит о том, что решение, которое я дал, создаст неограниченные переменные во входном списке с предыдущим элементом, найденным в списке (что позволит получить больший подсписок). Это не должно быть проблемой, если в вашем списке ввода нет неконкретизированных переменных. - person gusbro; 13.03.2012
comment
@ user1257768: Проблема с решением gusbro в том, что это не полное отношение. Это работает, только если в списке нет переменных. Но с переменными вы получаете не обобщенный ответ, а что-то другое. решение gusbro было бы намного лучше, если бы в таком случае оно приводило к ошибке инстанцирования. - person false; 13.03.2012
comment
Я изменил первое предложение плато/6, чтобы иметь дело с переменными без конкретизации. - person gusbro; 13.03.2012
comment
@gusbro: Это никогда не помогает! Если вы не можете правильно обрабатывать переменные, сгенерируйте ошибку для случая, который вы не можете решить. Подумайте: plateau([A,B,C],I,Len). вы получаете ответы на I = 1, но не на I = 2! Вы можете исправить это как: ( (Item\=NItem;Item==NItem) -> true ; throw(error(instantiation_error,plateau/3)) ) - person false; 14.03.2012
comment
@gusbro: при отсутствии ограничений (A\=B;A==B) эквивалентен (нестандартному) встроенному ?=(A,B). - person false; 14.03.2012
comment
@false: но ваше решение также не работает в случае [A, B, C]. Это дает только один результат I=1, Len=2 - person gusbro; 14.03.2012
comment
@gusbro: я не согласен. Есть 3 ответа: два с Prefix = [] и один с Prefix = [A]. - person false; 14.03.2012
comment
@false, зачем вообще обрабатывать переменные в списке? ОП никогда не говорил, что в списке будут какие-либо вары. Они просто хотят изучить Пролог в самом простом виде; обработка vars экстралогична, не так ли? plateau([a,a,X], 1, 2). терпит неудачу, потому что так и должно быть. Плато — это максимальная подпоследовательность, а максимальная подпоследовательность [a,a,X] — это [a,a,a]. Длина 3. Вы читаете это по-другому, но эта интерпретация столь же верна. - person Will Ness; 16.03.2012
comment
@WillNess: 1st Если вы используете Пролог в его простейшем виде (например, в начале St&Sh), вы никогда не будете использовать if-then-else, как это сделал gusbro выше. Такой if-then-elese является (в этом контексте) экстралогичным. С другой стороны, использование переменных — это то, что мы всегда делаем в Прологе. Вы не должны заявлять об этом. Также другое ваше утверждение ложно - см. следующий комментарий. - person false; 16.03.2012
comment
@WillNess: Мы можем не согласиться с самим понятием максимума. То есть, как переменные количественно определяются в этом случае. Однако, независимо от способа количественной оценки переменных, plateau([a,X,b,b], 2, 3). должно быть успешным, но определение gusbro здесь не работает. - person false; 16.03.2012
comment
@false Я должен был сказать мета-логично. Но в любом случае вопрос недоопределен. Ваша интерпретация верна, но другая будет рассматривать создание экземпляров vars последовательно (например, ваше умное использование maplist(=(_),Es) ) и рассматривать X как необходимость создания экземпляра a раз и навсегда, и поэтому ваш пример рассматривается как должен потерпеть неудачу. Одинаково справедливо. (мой ответ тоже не работает). - person Will Ness; 17.03.2012
comment
@Will Ness: из OP я не вижу, что plateau([a,X,b,b], 2, 3). должен потерпеть неудачу, и никаких недоработок в этом отношении. И я попытался использовать те самые аргументы, которые вы использовали для оправдания того, что plateau([a,a,X], 1, 2). должно потерпеть неудачу. Так может быть, мы можем только соглашаться не соглашаться? - person false; 17.03.2012
comment
@false линейная интерпретация: плато – это последовательность равных элтов максимальной длины; начать с начала списка; в plateau([a,X,b,b], 2, 3) должно быть X=a; невозможно переустановить X; plateau([a,X,b,b], 2, 3). должен потерпеть неудачу -- plateau([a,X,b,b],I,N). должен дважды завершиться успешно. Ваша нелинейная интерпретация: начните с индексов - необходимо повторно создать экземпляр X с другими значениями: plateau([a,X,b,b],I,N). должно быть успешным три раза. Сравните с: maplist( =(_), Es) является линейным: переменная anon не переустанавливается для последующих elts. - person Will Ness; 17.03.2012
comment
@false, поэтому, конечно, каждая интерпретация верна. И это то, что не указано. (т.е. наличие варов в списке). -- чтобы пояснить приведенный выше комментарий: linear: успешно дважды, с одинаковым значением для X в обоих случаях (т. е. a); нелинейный: успешно три раза, каждый раз со своим значением для X (например, a ; b ; c). - person Will Ness; 17.03.2012
comment
@false (продолжение) и на самом деле, почему бы и не 4 раза, с a ; b ; c ; d или пятью, шестью и т.д. Таким образом, кажется, что нелинейная интерпретация распутывается. - person Will Ness; 17.03.2012
comment
@WillNess: начать с начала списка ... не могу переустановить X. Я не могу связать это с тем, что заявил ОП, а именно: выводит все подсписки входного списка, где .... - person false; 17.03.2012
comment
@false ... где каждый подсписок имеет длину › 1 и не может быть расширен до большего подсписка. Так что все зависит от нашей интерпретации слова «нельзя». Вы говорите, что plateau([a,X,b,b], I,N) должен преуспеть дважды, с X=a,I=1,N=2, а затем X=b,I=2,N=3; Я говорю, что после того, как он преуспеет один раз с X=a, он больше не может X предполагать b и должен добиться успеха во второй раз с X=a,I=3,N=2. Дело вкуса. Но обратите внимание, что OP явно рассматривает I,N как переменные output: он также выводит начальную позицию ... . И они никогда вообще не говорят, что в списке может быть uninst'd vars, вообще. - person Will Ness; 17.03.2012
comment
@false IOW, aa и bbb не являются двумя подсписками одного и того же входного списка [a,X,b,b]. Правильно?? (странно... думаю о наблюдаемом значении спина прямо сейчас, о суперпозиции состояний и т.д...) - person Will Ness; 17.03.2012

Это просто и понятно. Считаем от 1; плато — максимальная подпоследовательность равных элементов длиной не менее 2; идем дальше по списку. Просто запишите это:

plateau(L,I,N):- plateau(L,1,I,N).                     % count from 1

plateau([A,A|B],I1,I,N):- !, more_elts(A,B,2,K,C),     % two equals, or more
    (I is I1, N is K ; plateau(C,I1+K,I,N)).
plateau([_|B],I1,I,N):- plateau(B,I1+1,I,N).           % move along the list

more_elts(A,[A|B],I,K,C):- !, more_elts(A,B,I+1,K,C).
more_elts(_,B,I,I,B).

обновление: предполагается, что все элементы входного списка nonvar/1. Наличие неэкземплярных переменных в качестве элементов входного списка здесь делает понятие «подсписок» сложным и расплывчатым. Например, каковы подсписки [a,X,b,b]? Могут ли [a,a] и [b,b,b] оба быть подсписками одного и того же входного списка? (это мне как-то напоминает наблюдаемые значения спина, суперпозиции состояний и т.д.... Когда выбрано направление наблюдения спина, его уже нельзя изменить... ср. все разговоры об "измерении" и квантовая механика в sokuza-kanren.scm (нашел ссылку здесь))

person Will Ness    schedule 16.03.2012