Текст книги "Программирование на языке пролог"
Автор книги: У. Клоксин
Соавторы: К. Меллиш
Жанр:
Программирование
сообщить о нарушении
Текущая страница: 20 (всего у книги 26 страниц)
9.3. Запись грамматических правил в Прологе
Специальная форма записи грамматических правил в языке Пролог была разработана, чтобы помочь программисту в написании синтаксических анализаторов, использующих описанные только что методы. Такая форма записи облегчает чтение программы, так как скрывается вся информация, не представляющая интереса. В силу того, что эта форма записи является более краткой, чем обычная для Пролога, вероятность сделать глупую опечатку также меньше, если для написания синтаксических анализаторов используются грамматические правила.
Хотя форма записи грамматических правил, о которой здесь идет речь, вводится сама по себе, важно помнить, что это лишь сокращенная запись обычных Пролог-программ. Вы можете воспользоваться грамматическими правилами либо потому, что ваша Пролог-система уже знает, как их обрабатывать, либо потому, что имеется библиотечный пакет программ, позволяющий использовать специальную форму предиката consult.В любом случае, способ обработки грамматических правил системой заключается в распознавании их при вводе и трансляции на обычный Пролог. Таким образом, грамматические правила превращаются в обычные утверждения Пролога, хотя, естественно, выглядят несколько по-иному по сравнению с тем, что программист обычно вводит в систему.
Рассматриваемая форма записи основывается на форме записи, использованной в начале этой главы для контекстно-свободных грамматик. Действительно, если бы грамматика, представленная в разд. 9.1 (она воспроизведена ниже), была введена в Пролог-систему, то она (грамматика) была бы оттранслирована в точности в те же самые утверждения, которые составляют последнюю версию программы синтаксического анализатора, приведенной в конце предыдущего раздела.
предложение–› группа_существительного, группа_глагола.
группа_существительного–› определитель, существительное.
группа_глагола–› глагол.
группа_глагола -› глагол, группа_существительного, определитель–› [the].
существительное–› [man].
существительное–› [apple].
глагол–› [eats].
глагол–› [sings].
На самом деле грамматические правила представляют структуры языка Пролог с главным функтором '–›' который объявлен как инфиксный оператор. Все, что должна сделать Пролог-система,– это проверить, имеет ли вводимый терм (при использовании предиката consultи ему подобных) указанный функтор и, если это так, оттранслировать его в соответствующее утверждение.
Как происходит эта трансляция? Прежде всего, каждый атом, обозначающий класс словосочетания, должен быть оттранслирован в предикат с двумя аргументами – для входной последовательности и последовательности, остающейся после обработки, как в нашей программе. Затем, всякий раз, когда в правиле встречаются идущие одно за другим имена словосочетаний, соответствующие предикаты должны быть расположены так, чтобы аргументы показывали, что последовательность, которая остается после обработки одного словосочетания, образует входную последовательность для следующего словосочетания. И наконец, всякий раз, когда в правиле указывается, что словосочетание может быть реализовано как последовательность более мелких словосочетаний, аргументы должны говорить о том, что число слов, входящих в словосочетание, равно суммарному числу слов, входящих в более мелкие словосочетания, упоминаемые в правой части правила. Эти критерии гарантируют, например, что правило:
предложение-› группа_существительного, группа_глагола.
транслируется в утверждение:
предложение (S0, S):– группа_существительного(S0,S1), группа_глагола(S1,S).
или
Между началом S0 и S имеется предложение, если между началом S0 и S1 имеется группа существительного, и если между началом S1 и S имеется группа глагола.
Наконец, система должна знать, как транслировать правила, вводящие реальные слова. Трансляция таких правил производится путем включения слов в списки, образующие аргументы соответствующих предикатов, так что, например, правило
определитель–› [the].
транслируется в утверждение:
определитель(the|S],S).
Если программа синтаксического анализатора представлена в виде совокупности грамматических правил, то каким образом надо задать целевые утверждения, чтобы можно было обработать их программой? Так как известно, как грамматические правила транслируются в утверждения обычного Пролога, то можно выразить целевые утверждения обычным образом, добавив при этом дополнительные аргументы. Первый добавляемый аргумент представляет обрабатываемый список слов, а второй аргумент соответствует списку слов, оставшемуся после обработки, который как правило является пустым списком []. Таким образом, целевое утверждение можно задать так:
?– предложение([thе, man, eats,the,apple],[]).
?– группа_существительного([the,man,sings],X).
В некоторых реализациях Пролога в качестве альтернативы имеется встроенный предикат phrase,который просто добавляет дополнительные аргументы. Предикат определен следующим образом:
phrase (P,L) истинно, если список L является допустимым словосочетанием типа Р.
Таким образом, можно было бы заменить первое из приведенных выше целевых утверждений следующим:
?– phrase(прeдложeниe, [the,man,eats,the,apple]).
Заметим, что определение предиката phraseпредполагает, что обрабатывается весь список целиком, а список, оставшийся после обработки должен быть пустым. Поэтому второе целевое утверждение нельзя изменить, используя предикат phrase.Если в реализации Пролога нет встроенного предиката phrase,то его можно легко определить следующим образом:
phrase(P,L):– Goal =.. [P,L,[]], call(Goal).
Отметим однако, что данное определение будет не приемлемо, если используются более общие грамматические правила, о которых мы скажем в следующем разделе.
Упражнение 9.1.Может быть вы захотите определить на Прологепроцедуру translateтаким образом, что translate(X,Y)истинно, если X– грамматическое правило (подобное тем, с какими мы сталкивались в этом разделе), a Y– терм, представляющий соответствующее утверждение Пролога. Это упражнение довольно трудное. Процедуры подобные translateимеются в Пролог-системе либо как встроенные, либо в виде библиотечных программ. Но если вы действительно напишите процедуру translate,то это поможет вам понять, что представляет собой процесс трансляции.
9.4. Присоединение дополнительных аргументов
До сих пор рассматривался лишь довольно ограниченный класс грамматических правил. В этом разделе мы введем одно полезное расширение, позволяющее использовать в грамматических правилах дополнительные аргументы. Это «расширение» не выходит за рамки стандартных средств, предоставляемых в Прологе для записи грамматических правил.
Уже было показано, как имя некоторого класса словосочетаний, встретившееся в грамматическом правиле, транслируется в предикат, имеющий два дополнительных аргумента. Таким образом, грамматические правила приводят к множеству предикатов с двумя аргументами. Иногда при написании синтаксических анализаторов, помимо аргументов, служащих для представления обрабатываемой последовательности, могут потребоваться дополнительные аргументы, тем более, что предикаты в Прологе могут иметь произвольное число аргументов. Предлагаемая здесь форма представления грамматических правил обеспечивает такую возможность.
Давайте рассмотрим пример, показывающий, когда такие дополнительные аргументы полезны. Разберем задачу «согласования числа» между подлежащим и сказуемым предложения. Последовательности слов подобные
*The boys eats the apple.
*The boy eat the apple.
не являются грамматически правильными предложениями, даже если бы они и допускались грамматикой после незначительного ее расширения ('*' используется для обозначения грамматически неправильных предложений). Причина, по которой они не могут считаться грамматически правильными, состоит в том, что, если подлежащее в предложении употребляется в единственном числе, то и сказуемое в этом предложении должно быть глаголом в единственном числе. Аналогично, если подлежащее употребляется во множественном числе, то и сказуемое должно быть в форме множественного числа. Это можно было бы выразить в грамматических правилах, указав, что существуют два типа предложений: предложения с подлежащим в единственном числе и предложения с подлежащим во множественном числе. Предложение первого типа должно начинаться с группы существительного в единственном числе, в которую должно входить существительное, стоящее в форме единственного числа, и так далее. Это привело бы к следующему множеству правил:
предложение–› предложение_едч.
предложение–› предложение_мнч.
группа_существительного– ›группа_существительного_едч.
группа_существительного–› группа_существительного_мнч.
предложение_едч–› группа_существительного_едч, группа_глагола_едч.
группа_существительного_едч–› определитель_едч, существительное_едч.
группа_глагола_едч–› глагол_едч, группа_существительного.
группа_глагола_едч–› глагол_едч.
определитель_едч–› [the].
существительное_едч–› [boy].
глагол_едч–› [eats].
Аналогичное множество правил можно написать и на случай множественного числа. Такой способ согласования числа не очень изящен и скрывает тот факт, что структура предложений для единственного и множественного числа во многом совпадает. Более удачный способ состоит в том, чтобы связать с классами словосочетаний дополнительный аргумент, указывающий на использование единственного или множественного числа. Так предложение(едч)обозначает словосочетание предложение,употребленное в форме единственного числа. В общем случае, предложение (X)обозначает предложение, форма числа которого равна X. При этом правила согласования числа сводятся к условиям согласованности значений этих аргументов. Форма числа подлежащего в группе существительного должна совпадать с формой числа группы глагола и так далее. Переписав соответствующим образом грамматические правила, получаем:
предложение–› предложение(Х).
предложение(Х)– ›группа_существительного(Х), группа _глагола(Х).
группа_существительного(Х)–› определитель(Х), существительное(Х).
группа_глагола(Х)–› глагол(Х).
группа_глагола(X)–› глагол(Х), группа_существительного(Y).
существительное(едч)–› [boy].
существительное(мнч) -› [boys].
определитель(_)–› [the].
глагол(едч)–› [eats].
глагол(мнч)–›[eat].
Обратим внимание на способ, каким можно определить форму числа для the.С этого слова могло бы начинаться словосочетание, употребленное в форме как единственного, так и множественного числа, и поэтому форма числа этого слова может быть сопоставлена с чем угодно. Отметим также, что во втором правиле для предиката группа_глаголаимена переменных выражают факт, что форма числа группы глагола (которая должна быть согласована с формой числа подлежащего) определяется по форме числа глагола, а не по форме числа объекта, если таковой имеется.
Можно ввести аргументы, выражающие другую важную информацию помимо согласования числа подлежащего и сказуемого. Например, их можно использовать для хранения перечня элементов, составляющих предложения, встретившихся не на своем обычном месте, и тем самым обрабатывать ситуации, называемые лингвистами «смещением». Или же они могут быть использованы для отражения семантическиххарактеристик, указывающих, каким образом значение (смысл)словосочетания составляется из значений более мелких словосочетаний, входящих в исходное. Эти вопросы далее не будут обсуждаться, хотя в разд. 9.6 приведен простой пример использования семантической информации в синтаксическом анализаторе. Однако существует один момент, который здесь необходимо отметить. Лингвистам, возможно, интересно знать, что если в грамматические правила введены дополнительные аргументы, то нельзя гарантировать, что язык, определяемый этой грамматикой, по-прежнему остается контекстно-свободным языком, хотя довольно часто он будет таковым.
Представляется важной возможность использования дополнительных аргументов для возврата дерева разбора, являющегося результатом синтаксического анализа. В главе 3 было показано, как можно представить деревья в виде структур Пролога. Здесь это будет использовано при расширении синтаксического анализатора с целью включить в него построение дерева разбора. Польза деревьев синтаксического разбора в том, что они дают структурное представление предложения. Удобно писать программы, которые обрабатывают эти структурные представления подобно тому, как обрабатывались арифметические формулы и списки в гл. 7. Новая программа для грамматически правильного предложения, такого например, как
The man eats the apple.
строит в качестве результата структуру вида:
предложение(
группа_существительного(
определитель(thе), существительное(man)),
группа_глагола(
глагол(eats), группа_существительного(определитель(the),существительное(аррlе))
)
)
Чтобы добиться этого, необходимо лишь добавить дополнительные аргументы к каждому предикату, указывая, каким образом дерево, соответствующее всему словосочетанию в целом, конструируется из деревьев для частей этого словосочетания. Так первое правило можно изменить следующим образом:
предложение(Х,предложение(NP,VP))–› группа_существительного(X,NP), группа_глагола(X,VP).
Это правило указывает, что, если можно найти последовательность, составляющую группу существительного с деревом разбора NP,за которой следует последовательность, составляющая группу глагола, с деревом разбора VPто можно найти последовательность, составляющую полное предложение, и деревом разбора для этого предложения является предложениe(NP,VP).Или, в более процедурной формулировке: для того, чтобы выполнить разбор предложения необходимо найти группу существительного, за которой следует группа глагола, и затем объединить деревья разбора этих двух составляющих, используя функтор предложениедля построения дерева разбора предложения. Отметим, что аргументы X– это аргументы, использовавшиеся ранее для согласования формы числа, и решение поставить аргументы для построения дерева разбора после, а не перед ними является совершенно произвольным. Если вам трудно понять это расширение возможностей грамматических правил, то полезно напомнить, что последнее правило представляет собою всего лишь краткую запись следующего утверждения на языке Пролог:
предложение(Х,предложение(NР,VP),S0,S):-группа_существительного(Х,NР,S0,S1),
группа_глагола(Х,VР,S1,S).
где S0, S1и S– части входной последовательности слов. Аргументы, предназначенные для построения дерева разбора, можно ввести в правила грамматики обычным образом. Здесь приведен получившийся после этого фрагмент грамматики (аргумент, используемый для согласования формы числа, для ясности опущен):
предложение(предложение(NР,VP))–›группа _существительного (NP), группа _ глагола (VP).
группа_глагола(группа_глагола(V))–› глагол(V).
существительное(существительное(man)) – › [man].
глагол(глагол(eats))–› [eats].
Механизм трансляции, необходимый для перевода грамматических правил с дополнительными аргументами в утверждения Пролога, представляет простое расширение описанного ранее механизма. Ранее, для каждого класса словосочетаний создавался новый предикат с двумя аргументами, представляющими входную последовательность и последовательность, оставшуюся после обработки. Теперь надо создать предикат, имеющий на два аргумента больше, чем имеет соответствующее ему грамматическое правило. По соглашению, эти два дополнительных аргумента записываются в конце списка аргументов предиката (хотя в других Пролог-системах может использоваться иное соглашение). Таким образом, грамматическое правило
предложение(Х)–› группа_существительного(Х),группа_глагола(Х).
транслируется в утверждение
предложение(Х,S0,S):– группа_существительного(Х,S0,S1),группа_глагола(Х,S1,S).
При вызове целевых утверждений, содержащих грамматические правила, с самого верхнего уровня интерпретатора (т. е. из вопросов, задаваемых программистом.– Ред.)или из обычных правил Пролога, необходимо явным образом указать дополнительные аргументы, добавив их в конец списка аргументов. Таким образом, правильное целевое утверждение, содержащее обращение к функтору предложение,должно быть таким:
?– предложение(Х,[a,clergyman,eats,a, cake],[]).
?– предложение(Х, [every, bird, sings,and,pigs,can,fly],L).
Упражнение 9.2.Напишите новый вариант предиката phrase,допускающий использование грамматических правил с дополнительными аргументами. Этот предикат должен допускать целевые утверждения вида:
?– phrase(предложение(Х),[the, man,sings]).
9.5. Введение дополнительных условий
Рассматривавшиеся до сих пор грамматические правила, входящие в программу синтаксического анализатора, содержали информацию лишь о том, какая часть входной последовательности обрабатывается каждым из правил. Каждый элемент описания правил должен содержать указание о том, как обрабатывать два дополнительных аргумента, добавленных транслятором грамматических правил. Так что каждое целевое утверждение в результирующем утверждении Пролога обрабатывало определенную часть входной последовательности. Иногда может возникнуть желание определить целевые утверждения иного типа, не связанные непосредственно с обработкой входной последовательности, и рассматриваемый формализм грамматических правил позволяет сделать это. В языке принято следующее соглашение: каждое целевое утверждение, заключенное в фигурные скобки {}, при трансляции не меняется.
Рассмотрим несколько примеров ситуаций, в которых было бы полезно использовать эту возможность. Мы покажем, как можно улучшить «словарь» синтаксического анализатора, т. е. его «знания» о словах обрабатываемого языка.
Прежде всего рассмотрим накладные расходы, связанные с введением нового слова в программу, в которой используются оба множества дополнительных аргументов (для согласования формы числа и для построения дерева разбора). Если нужно добавить новое слово, например banana, то для этого необходимо добавить хотя бы правило
существительное(едч, существительное(banana))–› [banana].
которое сводится к следующему факту на Прологе:
существительное(едч, существительное(banana),[banana|S],S).
Это слишком большой объем информации для определения каждого существительного, особенно когда мы знаем, что каждое существительное представляется лишь одним элементом входного списка и порождает небольшое дерево с функтором существительное.Значительно более экономичный способ представления основывается на разделении информации. Информация, общая для всех существительных, помещается в одном месте, а информация о конкретных словах в другом месте. Это можно сделать, если одновременно использовать возможности грамматических правил и обычного Пролога. Общая информация о том, каким образом существительные входят в другие словосочетания, представляется с помощью грамматического правила, а информация о том, какие слова являются существительными, представляется с помощью обычных утверждений Пролога. Этот подход можно применить к последнему примеру следующим образом:
существительное(S,существительное(N)) –› [N], {является_существительным(N,S)}.
где обычный предикат является_существительнымуказывает, какие слова являются существительными и в какой форме – единственного или множественного числа – они употребляются.
является_существительным(banana,едч).
является_существительным(bananas,мнч).
является_существительным(man,едч).
. . .
Давайте подробно разберем, что означает это грамматическое правило. Оно указывает, что словосочетание класса существительноеможет состоять из единственного слова N(переменная, определенная в списке), удовлетворяющего некоторому ограничению. Это ограничение заключается в том, что Nдолжно входить в число слов, представленных предикатом является_существительным,с указанием конкретной формы числа S. В этом случае, форма числа словосочетания также S, а порождаемое дерево разбора состоит из двух вершин: вершины существительноеи расположенной под ней вершины N. Почему целевое утверждение является_существительным (N,S)должно быть заключено в фигурные скобки? Потому, что оно выражает отношение, не имеющее ничего общего с обработкой входной последовательности. Если убрать фигурные скобки, то результат трансляции будет выглядеть примерно так:
является_существительным(N,S,S1,S2)
и это целевое утверждение никогда не было бы сопоставлено с утверждениями для является_существительным. Заключив целевое утверждение в фигурные скобки, мы тем самым предотвращаем какие-либо его изменения при трансляции. Так что правило будет правильно оттранслировано в следующее утверждение
существительное(S,существительное(N),[N|Посл],Посл):– является_существительным(N,S).
Несмотря на внесенное изменение, используемый способ обработки отдельных слов по-прежнему остается недостаточно изящным. Неудобство этого способа состоит в том, что для каждого вновь вводимого слова надо записывать два утверждения является_существительным– одно для формы единственного числа, а другое для формы множественного числа. Но в этом нет необходимости, поскольку для большинства существительных формы единственного и множественного числа связаны простым правилом:
Если X– существительное в форме единственного числа, то слово, получаемое в результате прибавления 's' к концу слова X, является формой множественного числа этого существительного.
Это правило можно использовать для пересмотра определения для существительное. В результате будет получено новое множество условий, которым должно удовлетворять слово N, для того, чтобы быть существительным. Так как эти условия связаны с внутренней структурой слова и не имеют никакого отношения к обработке входной последовательности, то они будут записаны с использованием фигурных скобок. Слова английского языка представляются как атомы Пролога и поэтому обсуждение вопроса разбиения слов на буквы сводится к рассмотрению литер, составляющих соответствующие атомы. Следовательно, в новом определении необходимо будет использовать предикат name. Улучшенный вариант правила выглядит следующим образом:
существительное(мнч,существительное(КореньN))–›
[N], {(name(М,Мнимя), присоединить(Едимя,"s",Мнимя), name(КореньN,Едимя), является_существительным(КореньN,едч))}.
Конечно, представленное здесь общее правило о форме множественного числа существительных справедливо не во всех случаях (например, множественное число для «fly» не совпадает с «flys»), И по-прежнему необходимо исчерпывающим образом описывать все исключения. Обратите внимание на использование двойных кавычек для представления списка, содержащего литеру 's'.Теперь необходимо определять утверждения для предиката является_существительнымлишь для существительных в единственном числе, если форма множественного числа для них образуется по указанному выше правилу. Заметьте, что в соответствии с приведенным определением в дерево разбора вставляется существительное в форме единственного, а не множественного числа. Это может оказаться полезным при последующей обработке дерева разбора. Обратите также внимание на то, как использованы фигурные скобки. В различных реализациях Пролога некоторые детали синтаксиса могут немного отличаться, но наиболее безопасно заключать содержимое фигурных скобок в дополнительные круглые скобки, если оно состоит более чем из одного целевого утверждения, и оставлять пропуск между фигурной скобкой и точкой, завершающей правило.
Большинство трансляторов синтаксических правил, помимо целевых утверждений, заключенных в фигурные скобки, оставляют неизменными и некоторые другие целевые утверждения. Так, обычно нет необходимости заключать в фигурные скобки '!' или дизъюнкции (;) целевых утверждений.