Современная электронная библиотека ModernLib.Net

Программирование на языке пролог

ModernLib.Net / Программирование / Клоксин У. / Программирование на языке пролог - Чтение (стр. 17)
Автор: Клоксин У.
Жанр: Программирование

 

 



portray(присоединить(А,В,С)):- writе('присоединить('), write(A), write(','), write(B), write(','), write('‹foo›)').


Каждый раз когда встретится цель X, содержащая предикат присоединить,приведенное утверждение будет обеспечивать успешное согласование цели portray(X),и вывод трассировочной информации будет полностью возложен на данное утверждение. В случае цели, содержащей любой другой предикат, цель port-гау(Х)не согласуется с базой данных и потому сведения об X будут выданы с помощью предиката write(X).Если бы приведенное выше утверждение присутствовало в базе данных, то соответствующая часть приведенного выше протокола трассировки выглядела бы следующим образом:


?- присоединить ([a],[b],X).

CALL присоединить([а],[b],‹fоо›)

CALL присоединить ([],[b],‹foo›)

EXIT присоединить([],[b],‹fоо›)

EXIT присоединить([a],[b],‹foo›)

X = [a,b];

REDO присоединить([a],[b],‹foo›)

REDO присоединить([],[b],‹foо›)

FAIL присоединить([],[b],‹foo›)

FAIL присоединить ([a],[b],‹foo›) нет

Т

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


?- присоединить ([а],[b],Х).

CALL присоединить([а],[b],_43)?

После вывода литеры '?' программа останавливается. Теперь от вас требуется ответ – команда, которой вы зададите одно из возможных действий. Если затребованное вами действие означает продолжение обычного выполнения программы, то она продолжит выполнение до тех пор, пока трассировка не дойдет до следующего управляемого события для прослеживаемого предиката. И опять вам будет задан вопрос вида:


CALL присоединить([],[b],_103)?


Одной из команд может быть выдача списка допустимых команд на терминал. Рассмотрим некоторые из них.

Выдача информации о цели

Первая группа команд предназначена для выдачи информации о цели в различных форматах. Как мы уже знаем, стандартным средством выдачи сведений о цели является предикат print,в рамках которого с помощью определяемого пользователем предиката portrayможно выводить нужные сведения в нужном формате. Однако у пользователя могут возникнуть сомнения в правильности утверждений, определяющих portray,или он может пожелать увидеть цель в обычной форме. Поэтому Пролог предоставляет команду, дающую вам возможность вывести сведения о текущей цело с помощью предиката writeили display. Вэтом случае программа не продолжает выполняться, а пользователя просят задать еще одну команду, которая укажет как следует продолжать выполнение программы. Как правило, этот диалог имеет следующий вид:


?- присоединить([a],[b],X).

CALL присоединить([а],[b],‹fоо›)? write

CALL присоединить([а],[b],_103)?


Обычно в качестве альтернативного способа вывода сведений о цели используют write.Предикат displayможет понадобиться в том случае, когда цель содержит много операторов, и вы забыли приоритеты их выполнения. В этом случае displayпоможет вам однозначно определить вложенность функторов.

Выдача информации о предшественниках

Предшественниками данной цели называются те цели, согласованность которых зависит от согласованности данной цели. На наших диаграммах с прямоугольниками это те цели, прямоугольники которых включают данную цель. Так, каждая цель имеет предшественника, который в свою очередь является одной из целей исходного вопроса – той целью, согласованию которой помогает текущая цель. Аналогично, когда речь идет о правиле, каждая цель, порожденная телом правила, имеет в качестве предшественника ту цель, которая сопоставлена с заголовком правила. Рассмотрим некоторые примеры предшественников. Обратимся к следующей простой программе обращения списка (которая рассматривалась в разд. 7.5):


обр([],[]).

oбp([H|T],L):- oбp(T,Z), присоединить(Z,[H],L).

присоединить([],X,Х).

присоединить([А|В],С,[А|D]):- присоединить(В,С,D).


Пусть мы задали исходный вопрос:


?- oбp([a,b,c,d],X). (A)


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


oбp([b,c,d],Z) (B)

присоединить(Z,[a],X) (C)


Поскольку второе утверждение будет снова использовано при согласовании (B),снова возникают две подцели:


oбp([c,d],Z1) (D)

присоединить(Z1,[a],Z) (E)


Их предшественниками являются цели (A)и (B).Заметим, что цель (C)не является их предшественником, поскольку от них непосредственно зависит только согласованность (B), от которой, в свою очередь, зависит согласованность (А).Цели (D)и (E)никак не влияют на согласованность (C). Когда процесс согласования исходного вопроса заходит уже достаточно далеко, возникает цель вида:


присоединить([с],[b],Y)


На этом этапе текущая цель и ее предшественники могут быть представлены в следующем виде:


oбp([a,b,c,d],_46) (цель A)

oбp([b,c,d],[d|_50]) (цель B)

присоединить([d,с],[b],[d|_51])

присоединить([с],[b],_52)


Прежде чем читать дальше, вам следует убедиться в том, что вы понимаете, почему это предшественники данной цели, а также почему у нее нет никаких других предшественников. С приведенным здесь изображением предшественников связана одна особенность, которая может проявиться и в вашей Пролог-системе. Существуют два способа выдачи информации о предшественнике на печать – при первом способе информация соответствует состоянию предшественника при первой попытке согласовать его, при втором – текущему состоянию, с теми значениями переменных, которые они получили в результате конкретизации. Здесь у нас принят второй способ. Когда выполнение впервые дошло до цели (B),второй аргумент предиката обрне был конкретизирован. Тем не менее, в списке предшественников этот аргумент показан как имеющий значение. Это объясняется тем, что теперь переменная, которая задана в этой позиции, оказалась конкретизированной, а именно, теперь мы выяснили, что для [b, с, d]первым элементом обращенного списка является d.

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

Изменение уровня трассировки

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

• Удаление всех контрольных точек. Это имеет тот же эффект, что и вызов цели nodebug(см. разд. 6.13).

• Отключение полной трассировки. Это имеет тот же эффект, что и вызов цели notrace(см. разд. 6.13).

• Включение полной трассировки. Это имеет тот же эффект, что и вызов цели trace(см. разд. 6.13).

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

• «creep» (ползти): Продолжить выполнение программы с полной трассировкой до тех пор, пока не поступит новое указание (при наступлении следующего управляемого события).

• «skip» (пропустить): Продолжить выполнение программы без выдачи какой-бы то ни было трассировочной информации до тех пор, пока не наступит какое-либо событие, относящееся к текущей цели.

• «leap» (перескочить); Продолжить выполнение программы без выдачи трассировочной информации до тех пор, пока либо не будет достигнута контрольная точка, либо не наступит событие, относящееся к текущей цели.

Первая из этих команд соответствует случаю, когда вы хотите, начиная с данного момента, тщательно проследить за ходом выполнения программы. Вторая команда соответствует случаю, когда вас не интересует ход согласования какой-либо цели, а нужно побыстрее перейти к тому, что происходит дальше. Третья команда относится к случаю, когда в ходе согласования некоторой цели выполняется большой объем не интересующей вас деятельности, но где-то в середине возникает интересующая вас цель (имеющая контрольную точку). Поэтому вам желательно пропускать все до тех пор, пока не будет достигнута нужная контрольная точка или (если программа ошибочна) пока текущая цель не окажется согласованной или несогласованной не достигнув контрольной точки. Ниже приводится пример использования команд «creep» и «skip». Предположим, что в простой программе сортировки, приведенной в разд. 7.7, есть ошибка, но при этом мы уверены, что наша программа порождения перестановок работает правильно. Если вы помните, определение программы наивсортначинается так:


наивсорт(Х,Y):- перестановка(Х,Y), отсортировано(Y),!.


Чтобы избежать необходимости просматривать массу детальных сведений о том, как работает программа перестановка, можно воспользоваться командой «skip» и получить трассировку, которая начинается так:


CALL наивсорт([3,6,2,9,20],_45)? creep

CALL перестановка([3,6,2,9,20],_45)? skip

EXIT перестановка([3,6,2,9,20],[3,6,2,9,20])? creep

CALL упорядочено(ЕЗ,6,2,9,20])? creep

CALL упорядочено(0,[3,6,2,9,20])? creep

CALL 0‹3?

. . .


Вмешательство в процесс согласования цели

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

• «retry» (повторить): Если вы задали команду «retry» после какого-либо события для некоторой цели, то Пролог вернется к тому месту, где он первоначально осуществил ВЫЗОВ (событие CALL) этой цели. Все будет в точности так же, как и в момент первоначального появления этой цели (кроме каких-либо добавлений к базе данных, которые могли быть сделаны). Это означает, что вы можете еще раз проследить за тем, что происходит при согласовании данной цели. На практике обычно сочетают использование команд «retry» и «skip». Если вы сомневаетесь в том, что ошибка возникает при согласовании некоторой цели, вы можете сперва пропустить (с помощью команды «skip») трассировку процесса ее согласования. Это означает, что вы не намерены пробираться через массу трассировочных данных, относящихся к цели, процесс согласования которой выполняется совершенно правильно. Если же возникла ошибка, и цель либо не согласуется с базой данных, либо дает неверный результат, то у вас сохраняется возможность после всего этого (с помощью команды «retry») вернуться назад и проследить за всем более внимательно.

• «or» (или): Эта команда, в точности как ';', означает просьбу о выборе альтернативного решения некоторого вопроса. Если вы находитесь в точке ВЫХОДа из цели (событие EXIT), вы также можете попросить о выборе альтернативы. Таким образом, если известно, что первый найденный ответ не позволяет успешно завершить остальную часть программы, можно тут же попросить о поиске другого решения. Это означает, что вы сможете быстрее добраться до той части программы, где содержится ошибка. Без такого режима вам пришлось бы «караулить» возможную неудачу процесса согласования после того, как была найдена первая альтернатива.

• «fail» (неудача): Эта команда в основном используется при наступлении события CALL. Если вы знаете, что данная цель в конце концов окажется несогласованной с базой данных, и что эта цель для вас не представляет интереса, то вы можете тут же принудительно завершить процесс согласования неудачно, задав эту команду.

Ниже приводится пример использования различных рассмотренных команд при изменении порядка согласования одного вопроса:


?- принадлежит(Х,[а,b,с]), принадлежит(Х,[b,d,е]).

CALL принадлежит(_ 44,[a, b,с])? creep

EXIT принадлежит(a, [а,b,с])? or

REDO принадлежит(a, [а,b,с])? creep

CALL принадлежит(_44,[b,с])? fail

FAIL принадлежит(_44,[b,с])? creep

FAIL принадлежит(_44,[a,b,с])? retry

CALL принадлежит(_44,[а,b,с])? creep

EXIT принадлежит(а,[а,b,с])? creep

CALL принадлежит(а,[d,с,е])? fail

FAIL принадлежит(a,[d,c,e])? creep

REDO принадлежит(а,[а,b,с])? creep

CALL принадлежит(_44,[b,с])? creep

EXIT принадлежит(b,[b,с])? or

REDO принадлежит(b,[b,с])? creep

CALL принадлежит(_ 44,[с])? fail

FAIL принадлежит(_44,[с])? retry

CALL принадлежит(_44,[с])? creep E

XIT принадлежит(с,[с])? creep

EXIT принадлежит(с,[b,c])? creep

EXIT принадлежит(c,[a,b,с])? creep

CALL принадлежит(c,[d,c,e])? creep

CALL принадлежит(c,[c,e])? creep

EXIT принадлежит(c,[c,e])? creep

EXIT принадлежит(c,[d,c,e])? or

REDO принадлежит(c,[d,c,e])? creep

REDO принадлежит(c,[c,e])? creep

CALL принадлежит(c,[e])? creep

CALL принадлежит(c,[])? creep

FAIL принадлежит(c,[])? creep

FAIL принадлежит(c,[е])? creep

FAIL принадлежит(c,[с,е])? retry

CALL принадлежит(c,[с,e])? creep

EXIT принадлежит(c,[c,e])? creep

EXIT принадлежит(с,[d,с,е])? creep

Другие команды

Рассмотрим другие возможные команды, которые могут быть доступны вам при наступлении управляемого события;

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

• «abort» (аварийное завершение): Эта команда вызывает прекращение выполнения всех ваших текущих программ, и вы «проваливаетесь» в Пролог-интерпретатор, который готов к вашему новому вопросу.

• «halt» (стоп): Эта команда вызывает полный выход из Пролога. Она может потребоваться вам, когда вы обнаружите ошибку и захотите для ее исправления отредактировать файл с той программой, где находится ошибка.

Заключение

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

1. За какими целями вы собираетесь следить? Если вы хотите следить за всем (задав полную трассировку с помощью предиката trace), то вы можете захлебнуться тем потоком информации, который будет поступать на ваш терминал. С другой стороны, если прослеживать только то, что происходит с небольшим числом предикатов (расставляя контрольные точки с помощью предиката spy), то можно пропустить момент, когда программа начинает работать неверно. Вероятно, лучшим решением является компромисс между аккуратным использованием контрольных точек, позволяющих сузить район поиска, и полной трассировкой на заключительном этапе, что позволяет точно обнаружить место ошибки.

2. Какой уровень управления выполнением программы вы намерены осуществить через терминал? Если задать все типы событий как неуправляемые, то вы не сможете оказать никакого влияния на ход выполнения программы, которая быстро проскочит через место ошибки прежде чем вы сможете это заметить, а тем более – внимательно рассмотреть. С другой стороны, если вы зададите все события как управляемые, то вам придется постоянно подталкивать программу, сообщая ей, что нужно продолжить выполнение при каждом новом событии.

3. Намерены ли вы задать специальные средства вывода информации о прослеживаемых целях? Это может оказаться полезным в тех случаях, когда некоторые из целей содержат в качестве аргументов чрезмерно большие структуры, которые не представляют особого, интереса, и только отвлекают внимание от действительно интересных аргументов. В этом случае для подавления выдачи ненужной информации можно воспользоваться возможностями предиката portray.

8.5. Фиксация ошибок

Если вы проследили за ходом выполнения программы и обнаружили, что она работает неверно, то вам захочется зафиксировать ошибку и запустить программу снова. Предположим, что ваша программа имеет разумные размеры, тогда она скорее всего уже хранится в файле на диске. Чтобы изменить эти файлы, вам придется использовать программу редактирования файлов. Здесь имеются две возможности:

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

• Если ваша Пролог-система не позволяет вернуться к прежнему состоянию после использования редактора и изменения ваших программных файлов, то вам придется запустить Пролог и применить предикат consultко всем вашим программным файлам, находящимся на внешней памяти.

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


?- [filel,file2,file3].

?- [file4,file5,fileб].


то в результате будут считаны в память все файлы file1, file2, file3, file4, file5, file6.

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

Ниже приводится небольшой пример того, как можно использовать предикаты consultи reconsult, чтобы внести изменения в программу с терминала. Этот сеанс работы начинается, когда в базе данных программиста нет ни одного утверждения.


?- присоединить([а,b,с,d],[е],Х).

нет

?- consult (user).

присоединить([А|В],С,[А|D]):- присоединить(А,С,D).

присоединить([],Х,Х).

обр([],[]).

обр([А|В],С):- o6p(B,D), присоединить(D,[А],С).

/* ввод литеры – признак конца файла */

да

?- обр([a,b,c,d,e],X).

нет

?- присоединить([а,b,с,d,е], [f],Х).

нет

?- присоединить([],[а,b,-],Х).

X = [а,b,с]

да

?- reconsult(user)

присоединить([А|В],С,[А|D]):- присоединить(В,С,D).

/* ввод литеры – признак конца файла */

да

?- oбp([a,b,c,d],X).

нет

?- consult (user).

присоединить([],Х,Х).

/* ввод литеры – признак конца файла */

да

?- oбp([a,b,c,d,e],X). X = [e,d,c,b,a].

да


В приведенном сеансе работы наш беспечный программист начинает с ввода через терминал утверждений для предикатов присоединитьи обр.Конечно, он мог бы сначала ввести их в файл, а затем предложить Прологу применить к этому файлу предикат consult.Впрочем, для такого маленького примера этого, может быть, и не стоило делать. К несчастью, в первом утверждении для присоединитьдопущена ошибка – в цели задано А, хотя на этом месте должно быть В. Эта ошибка обнаруживается, когда система не может ответить на вопросы, содержащие присоединитьи обр.Каким-то образом программист догадывается, что егоопределение присоединитьневерно (в реальном сеансе для этого, вероятно, потребовалось бы использовать средства отладки). Так или иначе, он решает заменить имеющееся определение новым, используя для этого предикат reconsult.К сожалению, в своем новом определении он забывает задать граничное условие (случай []). Поэтому программа по-прежнему не работает. К этому моменту исходное определение для присоединить,состоящее из двух утверждений, заменено новым определением из одного утверждения, которое оказалось неполным. Наш программист замечает, что он допустил ошибку и что ситуацию можно исправить, просто добавив к уже имеющемуся определению одно утверждение. Это делается также с помощью предиката consult.На этот раз программа заработала.

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

ГЛАВА 9. ИСПОЛЬЗОВАНИЕ ГРАММАТИЧЕСКИХ ПРАВИЛ В ПРОЛОГЕ

9.1. Проблема синтаксического анализа

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

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

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

предложение--› группа_существительного, группа_глагола.

группа_существительного--› определитель, существительное.

группа_глагола--› глагол, группа_существительного.

группа_глагола--› глагол.

определитель--› [the].

существительное--› [apple].

существительное--› [man].

глагол-- [eats].

глагол--› [sings].


Рис. 9.1.

Эта грамматика состоит из множества правил, каждое из которых записано в отдельной строке. Каждое правило определяет форму словосочетания определенного вида. Первое правило показывает, что предложение состоит из словосочетания, называемого группа_существительного,за которым следует словосочетание, называемое группа_глагола.Эти два словосочетания есть не что иное, как подлежащееи сказуемоепредложения (см. рис. 9.1).

Чтобы представлять, что значит правило в контекстно-свободной грамматике, их следует читать следующим образом: X--›Yкак « X может иметь вид Y», а ' X, Y' как « X, за которым следует Y». Так первое правило может быть прочитано:

предложение может иметь вид: группа_существительного, за которой следует группа_ глагола.

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

группа_существительного может иметь вид: определитель, за которым следует существительное.

Неформально, группа существительного – это группа слов, служащая для обозначения некоторого объекта (или объектов). Такая группа содержит слово – существительное, которое определяет главный класс, которому принадлежит этот объект. Так « the man» (человек) обозначает человека, « the program» (программа) обозначает программу и так далее. Кроме того, в соответствии с приведенной грамматикой, существительному должна предшествовать группа слов, названная «определитель» (см. рис. 9.2). Аналогично, внутренняя структура словосочетания группа_глаголаописывается соответствующими правилами. Заметим, что для этого словосочетания существуют два правила. Это вызвано тем, что оно может принимать два вида. Словосочетание группа_глаголаможет содержать словосочетание группа_существительного,как в предложении «the man eats the apple»(человек ест яблоко), или группа_существительногоможет отсутствовать, как в предложении «the man sings»(человек поет).

Для чего нужны остальные правила грамматики? Эти правила показывают, как некоторые словосочетания могут быть построены из настоящих слов, а не сводят их к совокупности более мелких словосочетаний. Слова английского языка записываются в квадратных скобках, так что правило:

определитель--› [the].

можно читать так:

определитель может иметь вид: словоthe.

Теперь, когда мы получили достаточно полное представление о грамматике, мы можем поговорить о том, какие последовательности слов являются грамматически правильными предложениями в соответствии с приведенной грамматикой. Наша грамматика очень проста и нуждается в целом ряде расширений. Основной недостаток заключается в том, что она допускает предложения, составленные лишь из пяти различных слов. Если мы хотим определить, является ли заданная последовательность слов предложением, в соответствии с тем, как оно определяется грамматикой, то необходимо применить первое правило, чтобы получить ответ на следующий вопрос:

можно ли разбить заданную последовательность на два словосочетания таким образом, чтобы первое из них было группа_существительного, а второе группа_глагола?

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

Рис. 9.2.


  • Страницы:
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24