Программирование на языке Пролог для искусственного интеллекта - Иван Братко
Шрифт:
Интервал:
Закладка:
• Следует применять мнемонические имена процедур и переменных. Они должны отражать смысл отношений и роль объектов данных.
• Существенное значение имеет расположение текста программы. Для улучшения читабельности программы нужно постоянно применять пробелы, пустые строки и отступы. Предложения, относящиеся к одной процедуре, следует размещать вместе в виде отдельной группы строк; между предложениями нужно вставлять пустую строку (этого не нужно делать, возможно, только в случае перечисления большого количества фактов, касающихся одного отношения); каждую цель можно размещать на отдельной строке. Пролог-программы иной раз напоминают стихи по эстетической привлекательности своих идей и формы.
• Стилистические соглашения такого рода могут варьироваться от программы к программе, так как они зависят от задачи и от личного вкуса. Важно, однако, чтобы на протяжении одной программы постоянно применялись одни и те же соглашения.
• Оператор отсечения следует применять с осторожностью. Если легко можно обойтись без него — не пользуйтесь им. Всегда, когда это возможно, предпочтение следует отдавать "зеленым отсечениям" перед "красными". Как говорилось в гл. 5, отсечение называется "зеленым", если его можно убрать, на затрагивая декларативный смысл предложения. Использование "красных отсечений" должно ограничиваться четко определенными конструкциями, такими как оператор not или конструкция выбора между альтернативами. Примером последней может служить
если Условие то Цель1 иначе Цель2
С использованием отсечения эта конструкция переводится на Пролог так:
Условие, !. % Условие выполнено?
Цель1; % Если да, то Цель1
Цель2 % Иначе - Цель2
• Из-за того, что оператор not связан с отсечением, он тоже может привести к неожиданностям. Поэтому, применяя его, следует всегда помнить точное прологовское определение этого оператора. Тем не менее, если приходится выбирать между not и отсечением, то лучше использовать not, чем какую-нибудь туманную конструкцию с отсечением.
• Внесение изменений в программу при помощи assert и retract может сделать поведение программы значительно менее понятным. В частности, одна и та же программа на одни и те же вопросы будет отвечать по-разному в разные моменты времени. В таких случаях, если мы захотим повторно воспроизвести исходное поведение программы, нам придется предварительно убедиться в том, что ее исходное состояние, нарушенное при обращении к assert и retract, полностью восстановлено.
• Применение точек с запятой может затемнять смысл предложений. Читабельность можно иногда улучшить, разбивая предложения, содержащие точки с запятой, на несколько новых предложений, однако за это, возможно, придется заплатить увеличенном длины программы и потерей в ее эффективности.
Для иллюстрации некоторых положений данного раздела рассмотрим отношение
слить( Спис1, Спис2, Спис3)
где Спис1 и Спис2 — упорядоченные списки, а Спис3 — результат их слияния (тоже упорядоченный). Например:
слить( [2, 4, 7], [1, 3, 4, 8], [1, 2, 3, 4, 4, 7, 8] )
Вот стилистически неудачная реализация этого отношения:
слить( Спис1, Спис2, Спис3) :-
Спис1 = [], !, Спис3 = Спис2;
% Первый список пуст
Спис2 = [], !, Спис3 = Спис1;
% Второй список пуст
Спис1 = [X | Остальные],
Спис2 = [Y | Остальные],
( X < Y, !,
Z = X, % Z - голова Спис3
слить( Остальные1, Спис2, Остальные3 );
Z = Y,
слить( Спис1, Остальные2, Остальные3 ) ),
Спис3 = [Z | Остальные3].
Вот более предпочтительный вариант, не использующий точек с запятой:
слить( [], Спис, Спис).
слить( Спис, [], Спис).
слить( [X | Остальные1], [Y | Остальные2], [X | Остальные3] ) :-
X < Y, !,
слить(Остальные1, [Y | Остальные2], Остальные3).
слить( Спис1, [Y | Остальные2], [Y | Остальные3]): -
слить( Спис1, Остальные2, Остальные3 ).
8.3.2. Табличная организация длинных процедур
Длинные процедуры допустимы, если они имеют регулярную структуру. Обычно эта структура представляет собой множество фактов, соответствующее определению какого-либо отношения в табличной форме. Преимущества такой организации длинной процедуры состоят в том, что:
• Ее структуру легко понять.
• Ее удобно совершенствовать: улучшать ее можно, просто добавляя новые факты.
• Ее легко проверять и модифицировать (просто заменяя отдельные факты, независимо от остальных).
8.3.3. Комментирование
Программные комментарии должны объяснять в первую очередь, для чего программа предназначена и как ею пользоваться, и только затем — подробности используемого метода решения и другие программные детали. Главная цель комментариев — обеспечить пользователю возможность применять программу, понимать ее и, может быть, модифицировать. Комментарии должны содержать в наиболее краткой форме всю необходимую для этого информацию. Недостаточное комментирование — распространенная ошибка, однако, программу можно и перенасытить комментариями. Объяснения деталей, которые и так ясны из самого текста программы, являются ненужной перегрузкой.
Длинные фрагменты комментариев следует располагать перед текстом, к которому они относятся, в то время как короткие комментарии должны быть вкраплены в сам текст. Информация, которую в самом общем случае следует включать в комментарии, должна схватывать следующие вопросы:
• Что программа делает, как ею пользоваться (например, какую цель следует активизировать и каков вид ожидаемых результатов), примеры ее применения.
• Какие предикаты относятся к верхнему уровню?
• Как представлены основные понятия (объекты)?
• Время выполнения и требования по объему памяти.
• Каковы ограничения на программу?
• Использует ли она какие-либо средства, связанные с конкретной операционной системой?
• Каков смысл предикатов программы? Каковы их аргументы? Какие аргументы являются "входными" и какие — "выходными", если это известно? (В момент запуска предиката входные аргументы имеют полностью определенные значения, не содержащие не конкретизированных переменных.)
• Алгоритмические и реализационные детали.
8.4. Отладка
Когда программа не делает того, чего от нее ждут, главной проблемой становится отыскание ошибки (или ошибок). Всегда легче найти ошибку в какой-нибудь части программы (или в отдельном модуле), чем во всей программе. Поэтому следует придерживаться следующего хорошего принципа: проверять сначала более мелкие программные единицы и только после того, как вы убедились, что им можно доверять, начинать проверку большего модуля или всей программы.
Отладка в Прологе облегчается двумя обстоятельствами: во-первых, Пролог — интерактивный язык, поэтому можно непосредственно обратиться к любой части программы, задав пролог-системе соответствующий вопрос; во-вторых, в реализациях Пролога обычно имеются специальные средства отладки. Следствием этих двух обстоятельств является то, что отладка программ на Прологе может производиться, вообще говоря, значительно эффективнее, чем в других языках программирования.
Основным средством отладки является трассировка (tracing). "Трассировать цель" означает: предоставить пользователю информацию, относящуюся к достижению этой цели в процессе ее обработки пролог-системой. Эта информация включает:
• Входную информацию — имя предиката и значении аргументов в момент активизации цели.
• Выходную информацию — в случае успеха, значения аргументов, удовлетворяющих цели; в противном случае — сообщение о неуспехе.
• Информацию о повторном входе, т.е. об активизации той же цели в результате автоматического возврата.
В промежутке между входом и выходом можно получить трассировочную информацию для всех подцелей этой цели. Таким образом, мы можем следить за обработкой нашего вопроса на всем протяжении нисходящего пути от исходной цели к целям самого нижнего уровня, вплоть до отдельных фактов. Такая детальная трассировка может оказаться непрактичной из-за непомерно большого количества трассировочной информации. Поэтому пользователь может применить "селективную" трассировку. Существуют два механизма селекции: первый подавляет выдачу информации о целях, расположенных ниже некоторого уровня; второй трассирует не все предикаты, а только некоторые, указанные пользователем.