Регулярные Выражения Пролог
Я пытаюсь выполнить сопоставление регулярных выражений. У меня записаны все функции, но они работают не так, как должны. Из того, что я могу сказать, у него есть проблема, когда я пытаюсь сравнить список.
Например, " re_contains (a,a)."дает true (очевидно), как и" re_contains(union (a,b),a)."
но как только я делаю список, он терпит неудачу. "re_contains(seq (a,b), [a,b])." возвращать false. Следует добавить, перебирая все возможные комбинации, чтобы найти совпадение, но ни одна из этих функций не работает правильно. Это заставляет меня думать, что, возможно, я упускаю базовый случай. Но я думаю, что "re_contains (X, L) :- X == L." должен позаботиться об этом. Я, должно быть, ищу здесь что-то важное.
вот мой код:
re_contains(empty, []).
re_contains(X, L) :-
X == L.
re_contains(seq(X, Y), L) :-
append(L1, L2, L),
re_contains(X, L1),
re_contains(Y, L2).
re_contains(union(X, _), L) :-
re_contains(X, L).
re_contains(union(_, Y), L) :-
re_contains(Y, L).
re_contains(kleene(X), L) :-
append([Car|L1], L2, L),
re_contains(X, [Car|L1]),
re_contains(kleene(X), L2).
re_contains(kleene(_),[]).
2 ответов
append/3
раскол L
, а как L1
и L2
будут перечислены.
Я бы попытался заменить re_contains(X, L) :- X == L.
С re_contains(X, [X]).
после смены, re_contains(a,a).
не удастся.
вы представляете последовательность по-разному, и ваш сопоставитель не обеспечивает оба. На самом деле, единственными "работающими" случаями являются не последовательности.
есть несколько вопросов. Вот самые очевидные:
ввод текста.
Ваш предикат re_contains/2
ожидает список в качестве второго аргумента. Это re_contains(a,a).
успех-это скорее совпадение, чем намерение.
монотонности. другая проблема в том, что re_contains([a],[a])
успешно, но re_contains([X],[a])
не удается. Или, чтобы посмотреть на это под другим углом:re_contains([X],[a])
не удается, но X = a, re_contains([X],[a])
успешно. Добавляя цель X = a
мы специализировали запрос таким образом изначально запрос не должен провалиться.
тестирование на идентичность (==/2
) следует заменить равенством (=/2
) и обеспечение того, что у нас есть список. Итак:
re_contains(X, L) :- %X == L. X = L, append(X,_,_).
отметим, что append/3
используется здесь только для того, чтобы убедиться, что X - это список-фактические функции добавления не используются.
эффективность. третья проблема касается фактического способа представления конкатенации. Давайте просто посмотрим следующее правило:
re_contains(seq(X, Y), L) :- append(L1, L2, L), re_contains(X, L1), re_contains(Y, L2).
теперь предположим, что у нас есть список длины N
. Сколько ответов возможно для цели append(L1, L2, L)
? На самом деле N + 1
. И это, независимо от фактических значений. Теперь рассмотрим:
?- length(L,1000000), time(re_contains(seq([a],[]),[b|L])). % 2,000,005 inferences, 0.886 CPU in 0.890 seconds (100% CPU, 2258604 Lips) false.
re_contains/2
здесь требуется время, пропорциональное длине списка. Но достаточно посмотреть на первый элемент, чтобы понять, что это невозможно.
таким образом, проблема заключается в использовании append/3
. Существует простое эмпирическое правило для Prolog: если вы используете слишком много append/3
использовать dcgS - определенные грамматики предложения. Пожалуйста, посмотрите в тег для получения более подробной информации-и обратитесь к вводному тексту пролога. Чтобы дать вам начало, вот подмножество вашего определения:
re_contains(RE, L) :- phrase(re(RE), L). re([]) --> []. re([E]) --> [E]. re(seq(X,Y)) --> re(X), re(Y).
который больше не исследует весь список:
?- length(L,1000000), time(phrase(re(seq([a],[])),[b|L])). % 6 inferences, 0.000 CPU in 0.000 seconds (88% CPU, 127313 Lips) false.
кстати, здесь полный определение.
прекращение / non-прекращение. связанный с эффективностью свойство прекращения. В идеале запрос завершается, если набор решений может быть представлен конечно. То есть конечным числом ответов. ОК, это идеал к которому мы стремимся. Простой, но очень эффективный алгоритм выполнения Prolog иногда будет циклическим, когда возможно конечное число ответов. Понимание самой причины не-прекращения иногда очень хитрый. Обычные стратегии отладки - например, трассировка или переход с помощью отладчика - не работают, поскольку они показывают слишком много деталей. К счастью, есть лучшая техника:
вместо того чтобы смотреть на всю вашу программу, я буду смотреть только на очень маленький фрагмент. Этот фрагмент -срез отказ (Подробнее см. ссылку). Он намного меньше, но рассказывает довольно много об оригинальной программе - при условии, что это был чистая, монотонная программа:
Если фрагмент сбоя не завершается, то исходная программа не завершается.
поэтому, если мы найдем такой срез отказа, мы можем сразу сделать выводы обо всей программе. Даже не прочитав остальное!
вот такой интересный срез отказа:
...re_contains(X, L) :- false,X = Lre_contains(seq(X, Y), L) :- append(L1, L2, L), false,re_contains(X, L1),re_contains(Y, L2).re_contains(union(X, _), L) :- false,re_contains(X, L). ...
Рассмотрим теперь цель re_contains(seq([],[]),L).
в идеале, должен быть только один ответ L = []
и цель должна завершить. Однако петли, так как append(L1, L2, L)
не прекращает. Сравните это с DCG-решением, выше которого заканчивается для phrase(re(seq([],[])),L)
.