2013-02-28 2 views
2

Мы хотим построить предикат, который получает список L и номер N и является истинным, если N - длина самой длинной строки списка L. Например:Prolog - последовательность в списке

?- ls([1,2,2,4,4,4,2,3,2],3). 
    true. 

?- ls([1,2,3,2,3,2,1,7,8],3). 
    false. 

Для этого я построил -

head([X|S],X). % head of the list 
ls([H|T],N) :- head(T,X),H=X, NN is N-1 , ls(T,NN) . % if the head equal to his following 
ls(_,0) :- !. % get seq in length N 
ls([H|T],N) :- head(T,X) , not(H=X) ,ls(T,N). % if the head doesn't equal to his following 

Концепция просто - проверить, если голова равна его ниже, если да, то по-прежнему с хвостом и уменьшает N.

Я проверил мой код и он работает хорошо (игнорировать случаи, которые N = 1) -

ls([1,2,2,4,4,4,2,3,2],3). 
true ; 
false . 

Но true ответ не является конечным, и есть еще ответ после того, как я мог бы сделать это, чтобы вернуть конечное ответ ?

ответ

3

Пролог-мудрый, у вас есть несколько проблем. Во-первых, ваш предикат работает только при создании экземпляров обоих аргументов, что разочаровывает Prolog. Еще один ваш стиль - head/2 ничего не добавляет к [H|T]. Я также считаю, что этот алгоритм в корне ошибочен. Я не думаю, что вы можете быть уверены, что в хвосте списка нет последовательности длинной длины, не сохраняя неизменной копии угадываемой длины. Другими словами, второе, что указывает @Zakum, я не думаю, что для этого будет простое решение.

Так я бы подошел к проблеме. Первый вспомогательный предикат для получения максимального из двух значений:

max(X, Y, X) :- X >= Y. 
max(X, Y, Y) :- Y > X. 

Сейчас большая часть работы sequence_length/2 делает делегирована петлю, для базового случая пустого списка, за исключением:

sequence_length([], 0). 
sequence_length([X|Xs], Length) :- 
    once(sequence_length_loop(X, Xs, 1, Length)). 

звонок в once/1 гарантирует, что мы получим только один ответ. Это предотвратит использование предиката с использованием списков с последовательностями, одновременно делая предикат детерминированным, что вам и нужно. (Он имеет тот же эффект, что и красиво вырезанный).

базовый случай Loop в: скопировать аккумулятор для выходного параметра:

sequence_length_loop(_, [], Length, Length). 

Индуктивный случай # 1: у нас есть еще один экземпляр того же значения. Увеличьте аккумулятор и повторите попытку.

sequence_length_loop(X, [X|Xs], Acc, Length) :- 
    succ(Acc, Acc1), 
    sequence_length_loop(X, Xs, Acc1, Length). 

Индуктивный корпус №2: у нас другое значение. Вычислить длину последовательности оставшейся части списка; если он больше нашего аккумулятора, используйте это; в противном случае используйте аккумулятор.

sequence_length_loop(X, [Y|Xs], Acc, Length) :- 
    X \= Y, 
    sequence_length([Y|Xs], LengthRemaining), 
    max(Acc, LengthRemaining, Length). 

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

+0

Спасибо! это очень полезно. – URL87

+0

@ Daniel_Lyons: Я был бы рад, если вы мне что-нибудь объясните, , как вы сказали, 'once' гарантировать, что мы получаем только 1 ответ, но если мы опустим это' once' и останемся с - 'sequence_length_loop (X, Xs, 1, Length) ', что может быть проверено больше проверок, чтобы получить больше результатов? здесь мы просто отправляем в 'sequence_length_loop' только созданные экземпляры' (X, Xs, 1, Length) '... – URL87

+1

@ URL87 Мы получаем ложную точку выбора без' once', как и в оригинале. Я выбрал «один раз» здесь, потому что я нахожу его более аккуратным, чем введение сокращения после каждого из других целей, и он передает цель более четко, чем разрез после цели цикла. –

2

Как насчет добавления перерыва до последнего правила?

head([X|S],X). % head of the list 
ls([H|T],N) :- head(T,X),H=X, NN is N-1 , ls(T,NN) . % if the head equal to his following 
ls(_,0) :- !.           % get seq in length N 
ls([H|T],N) :- head(T,X) , not(H=X) ,ls(T,N),!.   % if the head doesn't equal to his following 

Работает для меня, хотя я не эксперт по Prolog.

// EDIT: кстати. попробуйте

14 ?- ls([1,2,2,4,4,4,2,3,2],2). 
true ; 
false. 

Выглядит ложно для меня, нет проверки, является ли N самой длинной последовательностью. Или я неправильно понял требования?

2

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

ls([E|Es], L) :- ls(E, 1, Es, L). 

ls(X, N, [Y|Ys], L) :- 
    ( X = Y 
    -> M is N+1, 
    ls(X, M, Ys, L) 
    ; ls(Y, 1, Ys, M), 
    (M > N -> L = M ; L = N) 
). 
ls(_, N, [], N).