Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Layt_teor_osnovy_form_yazykov.doc
Скачиваний:
2
Добавлен:
01.04.2025
Размер:
1.17 Mб
Скачать

2.4. Построение ка из нка.

Итак, при работе НКА возникает проблема, которая заключается в следующем: мы не знаем, какую выбрать дугу на каждом шаге, если существует несколько дуг с одинаковой пометкой. Теперь покажем, как из НКА построить КА, который как бы параллельно проверяет все возможные пути разбора и отбрасывает тупиковые. То есть если в НКА имеется, к примеру, выбор из трех состояний X, Y, Z, то в КА будет одно состояние [XYZ], которое представляет все три. Напомним, что возможные состояния на каждом шаге работы НКА - это подмножество полного множества состояний и что число различных подмножеств конечно.

Т е о р е м а 2.1. Пусть НКА=(K, VT, M, S, Z) допускает множество цепочек L. Определим КА F'=(K', VT,M',S',Z') следующим образом:

1. Алфавит состояний K' состоит из всех подмножеств множества K. Обозначим элемент множества K' через [S1,S2,...,Si], где S1,S2,..., Si - состояние из K. (Допустим впредь, что состояния S1,S2,...,Si расположены в некотором каноническом порядке таким образом, что, например, состояние из K' для подмножества {S1, S2}={S2, S1} - суть [S1, S2].)

2. Множество входных литер VT для F и F' одно и тоже.

3. Отображение M' определим как

M'([S1,S2,...,Si], T)=[R1,R2,..., Rj],

где M({S1,S2,...,Si}, T)={R1,R2,... Rj}.

4. Пусть S={S1,...,Sn}. Тогда S'=[S1,..., Sn].

5. Пусть Z={Sj,Sk,..., Sl}. Тогда Z'=[Sj,Sk,...,Sl].

Утверждается, что F и F' допускают одно и то же множество цепочек.

Рис.2.4. Диаграммы состояний.

П р и м е р 2.2. На рисунке (2.4) приведена диаграмма состояний НКА с начальным состоянием S и P и одним заключительным состоянием Z. Справа показана диаграмма состояний соответствующего КА, у которого начальное состояние [S,P], а множество заключительных состояний {[Z],[S,Z],[P,Z], [S, P,Z]}.

Заметим, что состояния [S] и [P,Z] можно исключить, так как нет путей, ведущих к ним. Таким образом, построенный КА не является минимальным - возможен автомат с меньшим числом состояний.

Отображение М (рис.2.4.а) Отображение M’(рис.2.4.б)

M(S,0)=P M’(S,0)=P

M(S,1)={S,Z} M’(S,1)=[S,Z]

M(P,0)= M’(SP,0)=[P, ]=P

M(P,1)=Z M’(SP,1)=[S,Z]

M(Z,0)=P M’(P,0)= 

M(Z,1)=P M’(P,1)=Z

M’(PZ,0)=P

M’(PZ,1)=PZ

M’(SZ,0)=P

M’(SZ,1)=[SPZ]

M’(SPZ,0)=P

M’(SPZ,1)=[SPZ]

M’(Z,0)=P

M’(Z,1)=P

3. Нисходящие распознаватели.

3.1. Нисходящий разбор с возвратами.

Алгоритм нисходящего разбора строит синтаксическое дерево, начиная с корня, постепенно спускаясь до уровня предложения, как было показано на рис.1.8. Этот пример демонстрирует простоту и наглядность основной идеи. Описание усложняется главным образом из-за вспомогательных операций, которые необходимы для того, чтобы выполнять возвраты с твердой уверенностью, что все возможные попытки построения дерева были предприняты. Чтобы свести осложнения к минимуму, опишем этот алгоритм образно. Вообразим, что на любом этапе разбора, в каждом узле уже построенной части дерева, находится по одному человеку. Люди, находящиеся в терминальных узлах, занимают места, соответствующие символам предложения.

Некоему человеку предстоит провести разбор предложения x. Прежде всего, ему необходимо отыскать вывод Z=>+x, где Z - начальный символ; следовательно, первым непосредственным выводом должен быть вывод Z=>y, где Z::=y - правило. Пусть для Z существуют правила

Z::=X1X2...Xn|Y1Y2...Ym|Z1Z2...Zl

Сначала человек пытается применить правило Z::= X1X2...Xn. Если нельзя построить дерево, используя это правило, он делает попытку применить второе правило Z::=Y1Y2...Ym. В случае неудачи он переходит к следующему правилу и т.д.

Как ему определить, правильно ли он выбрал непосредственный вывод Z=>X1X2...Xn? Заметим, что если вывод правилен, то для некоторых цепочек xi будет иметь место x=x1x2...xn, где Xi=>*xi для i=1, ...,n. Прежде всего, человек, выполняющий разбор, возьмет себе приемного сына M1, который должен найти вывод X1=>*x1 для любого x1, такого, что x= x1.... Если сыну М1 удалось найти такой вывод, он ( и любой из его сынов, внуков и т.д. ) закрывает цепочку x1 в предложении x и сообщает своему отцу об успехе. Тогда его отец усыновит М2, чтобы тот нашел вывод X2=>*x1. Сообщение об успехе, пришедшее от сына Мn, означает, что разбор предложения закончен.

Как быть, если Мi не удается найти вывод Xi=>*x1? В этом случае Мi сообщает о неудаче своему отцу; тот от него отрекается и дает старшему брату Мi, Мi-1 такое распоряжение: "Ты уже нашел вывод, но этот вывод неверен. Найди-ка мне другой". Если Мi-1 сумеет найти другой вывод, он вновь сообщит об успехе, и все продолжится по-прежнему. Если же Мi-1 сообщит о неудаче, отец отречется и от него, и тогда уже его старшего брата, Мi-2, попросят предпринять еще одну попытку. Если придется отречься даже от М1, значит непосредственный вывод Z=>X1X2...Xn был неверен, и человек, начинавший разбор, попытается воспользоваться другим выводом Z=>Y1...Ym.

Как же действует каждый из Мi? Положим, его целью является терминал Xi. Входная цепочка имеет вид x=x1x2...xi-1T..., где символы в x1,x2,...,xi-1 уже закрыты другими людьми. Мi проверяет, совпадает ли очередной незакрытый символ T с его целью Xi. Если это так, он закрывает этот символ и сообщает об успехе. Если нет, сообщает о неудаче.

Если цель Мi - нетерминал Xi, то Мi поступает точно так же, как и его отец. Он начинает проверять правые части правил, относящихся к нетерминалу, и, если необходимо, тоже усыновляет или отрекается от сынов. Если все его сыновья сообщают об успехе, то Мi в свою очередь сообщает об успехе своему отцу. Если отец просит Мi найти другой вывод, а целью является терминальный символ, то Мi сообщает о неудаче, так как другого такого вывода не существует. В противном случае Мi просит своего младшего сына найти другой вывод и реагирует на его ответ так же, как и раньше. Если все сыновья сообщат о неудаче, он сообщит о неудаче своему отцу.

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

Привлекательность этого метода (и его представления) в том и состоит, что каждый человек должен помнить лишь о своей цели, о своем отце, о своих сыновьях, а также о своем месте в грамматике и во входной цепочке. И никому не нужны точные сведения о том, что происходит в других местах. Это как раз и есть то, к чему мы вообще стремимся в программировании: в каждом сегменте программы или в подпрограмме необходимо заботиться о собственной входной и выходной информации и ни о чем более.

Для имитации усыновления и отречения от сынов в программе используется стек типа LIFO ( последним пришел, первым ушел ). Стек - основной механизм, используемый почти во всех типах распознавателей. Действительно, всякий раз, когда говорят о рекурсии, говорят о стеках. Стек - запоминающее устройство, в котором хранятся данные. Однако новые данные можно занести в стек только "сверху", проталкивая при этом вниз данные, уже находящиеся в нем. Соответственно можно ссылаться только на один верхний или несколько верхних элементов, и только их можно изменять. Если верхние элементы больше не нужны, их исключают, "поднимая" при этом вверх элементы, находившиеся ниже.

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

Обычно для реализации стека используются массив S и счетчик v. Прим v=0 стек пуст. При v=n, где n>0, в стеке находятся элементы S(1),S(2),..., S(n)- верхний элемент стека.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]