200 likes | 351 Views
Логическое программировыание. Презентация 9 Отладка Пролог-программ. Содержание. Основные критерии и правила написания программ Предикаты для отладки и трассировки программ Множество решений. Предикаты порождения. Критерии оценки программ.
E N D
Логическое программировыание Презентация 9 Отладка Пролог-программ
Содержание • Основные критерии и правила написания программ • Предикаты для отладки и трассировки программ • Множество решений. Предикаты порождения
Критерии оценки программ • Правильность. Хорошая программа в первую очередь должна быть правильной, т. е. она должна делать именно то, для чего предназначалась. Это требование может показаться тривиальным и самоочевидным. Однако в случае сложных программ правильность достигается не так часто. Распространенной ошибкой при написании программ является пренебрежение этим очевидным критерием, когда большее внимание уделяется другим критериям - таким, как эффективность. • Эффективность. Хорошая программа не должна попусту тратить компьютерное время и память. • Простота, читабельность. Хорошая, программа должна быть легка для чтения и понимания. Она не должна быть более сложной, чем это необходимо. Следует избегать хитроумных программистских трюков, затемняющих смысл программы. Общая организация программы и расположение ее текста должны облегчать ее понимание. • Удобство модификации. Хорошая программа должна быть легко модифицируема и расширяема. Простота и модульная организация программы облегчают внесение в нее изменений. • Живучесть. Хорошая программа должна быть живучей. Она не должна сразу "ломаться", если пользователь введет в нее неправильные или непредусмотренные данные. В случае подобных ошибок программа должна сохранять работоспособность и вести себя разумно (сообщать об ошибках). • Документированность. Хорошая программа должна быть хорошо документирована. Минимальная документация - листинг с достаточно подробными комментариями.
Правила написания программ Чтобы писать программы, которые легко читать, понимать, отлаживать и модифицировать, нужно придерживаться следующих правил: • Предложения для одного предиката нужно группировать вместе, разделяя определения пустой строкой. • Каждое предложение нужно начинать с первой позиции и делать отступ для частей предложения, не поместившихся в одной строке. • Предложения должны быть по возможности короткими. • Имена предикатов и переменных должны отражать их назначение. • Предикаты !, not, assert и retract следует применять с осторожностью. • Число дизъюнкций в программе следует уменьшать, разделяя правила на несколько предложений или вводя новые предикаты. • Комментарии должны отражать все возможные способы использования предиката,какие аргументы должны быть конкретизированы, какой тип значений допустим, какие аргументы являются выходными. • В первых предложениях определения должны анализироваться частные случаиприменения предиката, а первые цели в теле правила должны проверять наиболеепростые отношения.
После реализации… После написания программы рекомендуется: • проверить, что в конце каждого предложения стоит точка; • проверить имена и количество аргументов у каждого предиката, включая встроенные, так как неверное имя или пропущенный аргумент дает заведомо неуспешную цель; • проверить введенные операторы, их приоритет и ассоциативность, используя display, в сложных случаях поставить скобки. • Следует соблюдать стиль! Подчиняться при программировании некоторым стилистическим соглашениям нужно для того, чтобы • уменьшить опасность внесения ошибок в программы и • создавать программы, которые легко читать, понимать, отлаживать и модифицировать.
Использование рисунков • В поиске идей для решения задачи часто бывает полезным обратиться к ее графическому представлению. Рисунок может помочь выявить в задаче некоторые существенные отношения. После этого останется только описать на языке программирования то, что мы видим на рисунке. • Использование графического представления при решении задач полезно всегда, однако похоже, что в Прологе оно работает особенно хорошо. Происходит это по следующим причинам: • Пролог особенно хорошо приспособлен для задач, в которых фигурируют объекты и отношения между ними. Часто такие задачи естественно иллюстрировать графами, в которых узлы соответствуют объектам, а дуги - отношениям. • Естественным наглядным изображением структурных объектов Пролога являются деревья. • Декларативный характер пролог-программ облегчает перевод графического представления на Пролог. В принципе, порядок описания "картинки" не играет роли, мы просто помещаем в программу то, что видим, в произвольном порядке. (Возможно, что из практических соображений этот порядок впоследствии придется подправить с целью повысить эффективность программы.)
Комментирование • Программные комментарии должны объяснять в первую очередь, для чего программа предназначена и как ею пользоваться, и только затем - подробности используемого метода решения и другие программные детали. Главная цель комментариев - обеспечить пользователю возможность применять программу, понимать ее и, может быть, модифицировать. Комментарии должны содержать в наиболее краткой форме всю необходимую для этого информацию. Недостаточное комментирование - распространенная ошибка, однако, программу можно и перенасытить комментариями. Объяснения деталей, которые и так ясны из самого текста программы, являются ненужной перегрузкой. • Длинные фрагменты комментариев следует располагать перед текстом, к которому они относятся, в то время как короткие комментарии должны быть вкраплены в сам текст. Информация, которую в самом общем случае следует включать в комментарии, должна схватывать следующие вопросы: • Что программа делает, как ею пользоваться (например, какую цель следует активизировать и каков вид ожидаемых результатов), примеры ее применения. • Какие предикаты относятся к верхнему уровню? • Как представлены основные понятия (объекты)? • Время выполнения и требования по объему памяти. • Каковы ограничения на программу? • Использует ли она какие-либо средства, связанные с конкретной операционной системой? • Каков смысл предикатов программы? Каковы их аргументы? Какие аргументы являются "входными" и какие - "выходными", если это известно? (В момент запуска предиката входные аргументы имеют полностью определенные значения, не содержащие не конкретизированных переменных.) • Алгоритмические и реализационные детали.
Типичные ошибки К наиболее типичным ошибкам при разработке программ на языке Пролог можно отнести следующие ошибки. • После создания или изменения программы не выполняется ее преобразование во внутреннее представление с помощью reconsult. • Программа содержит циклические рекурсивные определения, которые требуют согласования цели, эквивалентной исходной, и часто связаны с левосторонней рекурсией. • Рассмотрены не все случаи для завершения рекурсии или указаны неправильные условия • Указано неверное количество аргументов у функтора, что приведет к неудаче при поиске правила для обработки структуры. • Результатом применения программ, содержащих ошибки, обычно является зацикливание или переполнение стека, отрицательные ответы или конкретизация переменных не теми ответами, которые ожидались.
Отладка программ • Когда программа не делает того, чего от нее ждут, главной проблемой становится отыскание ошибки (или ошибок). Всегда легче найти ошибку в какой-нибудь части программы (или в отдельном модуле), чем во всей программе. Поэтому следует придерживаться следующего хорошего принципа: проверять сначала более мелкие программные единицы и только после того, как вы убедились, что им можно доверять, начинать проверку большего модуля или всей программы. • Отладка в Прологе облегчается двумя обстоятельствами: • Пролог - интерактивный язык, поэтому можно непосредственно обратиться к любой части программы, задав пролог-системе соответствующий вопрос; • В реализациях Пролога обычно имеются специальные средства отладки. • Следствием этих двух обстоятельств является то, что отладка программ на Прологе может производиться, вообще говоря, значительно эффективнее, чем в других языках программирования.
Трассировка • Основным средством отладки является трассировка (tracing). "Трассировать цель" означает: предоставить пользователю информацию, относящуюся к достижению этой цели в процессе ее обработки пролог-системой. Эта информация включает: • Входную информацию - имя предиката и значении аргументов в момент активизации цели. • Выходную информацию - в случае успеха, значения аргументов, удовлетворяющих цели; в противном случае - сообщение о неуспехе. • Информацию о повторном входе, т. е. об активизации той же цели в результате автоматического возврата. • В промежутке между входом и выходом можно получить трассировочную информацию для всех подцелей этой цели. Т.о., мы можем следить за обработкой нашего вопроса на всем протяжении нисходящего пути от исходной цели к целям самого нижнего уровня, вплоть до отдельных фактов. • Такая детальная трассировка может оказаться непрактичной из-за непомерно большого количества трассировочной информации. Поэтому пользователь может применить селективную трассировку (указав трассируемый предикат).
Пример трассировки • Режим трассировки включается с помощью предиката trace, а выключается notrace; в качестве аргумента можно указать имя трассируемого предиката. • Предикат spy( P), «следи за Р» - устанавливает режим контролируемойпошаговой трассировки предиката Р. Обращение к spy применяют, когда хотят получить информацию только об указанном предикате и избежать трассировочной информации от других целей (выше/ниже уровня запуска Р). nospy( Р) прекращает "слежку" за Р. Обычный запуск: fact(1,1):-!. fact(N,F):- N1 is N-1, fact(N1,F1), F is F1*N. ?- fact(3,X). X = 6 ; No Запуск в режиме трассировки: ?- trace(fact). % fact/2: [call, redo, exit, fail] Yes [debug] 2 ?- fact(3,X). T Call: (7) fact(3, _G429) T Call: (8) fact(2, _L170) T Call: (9) fact(1, _L189) T Exit: (9) fact(1, 1) T Exit: (8) fact(2, 2) T Exit: (7) fact(3, 6) X = 6 ;
События трасировки В процессе выполнения программ на языке Пролог могут происходить следующие 4события: • Событие CALLфиксирует начало попытки Пролога согласовать цель с БД Печатается цель. • СобытиеEXITфиксирует момент, когда некоторая цель только что согласована с БД Печатается согласованная цель. • Событие REDOфиксирует момент, когда система возвращается к цели, пытаясь повторно согласовать ее с БД. Печатается согласованная цель • Событие FAIL фиксирует момент, когда попытка согласовать цель с БД заканчивается неудачно. Печатается цель.
Действия при отладке • При наступлении события Пролог печатает цель и ожидает ввода команды. Можно ввести одну из следующих команд: • h[elp] - помощь; • пробел - переключить окна; • c[reep] или Enter - идти к следующему событию; • s[kip] или Esc - не останавливаться до наступления события EXIT или FAIL для текущей цели (рекомендуется использовать для уже отлаженных предикатов); • a[bort] - завершить выполнение программы. ?- spy(fact). % Spy point on fact/2 Yes [debug] 2 ?- fact(3, X). Call: (7) fact(3, _G435) ? creep% нажали Enter ^ Call: (8) _L169 is 3-1 ? skip % нажали S ^ Exit: (8) 2 is 3-1 ? Abort % нажали A % Execution Aborted
Повышение эффективности работы программ • Прогон откомпилированной программы обычно имеет большую эффективность, чем интерпретация. Поэтому, если пролог-система содержит как интерпретатор, так и компилятор, следует пользоваться компилятором, если время выполнения критично. • Существует много способов повышения эффективности программы. Наиболее простые способы включают в себя: • изменение порядка целей и предложений • управляемый перебор при помощи введения отсечений • запоминание (с помощью assert) решений, которые требуется перевычислять • Более тонкие и радикальные методы связаны с улучшением алгоритмов (особенно, в части повышения эффективности перебора) и с совершенствованием структур данных. • Полезные идеи, относящиеся к повышению эффективности, обычно возникают только при достижении более глубокого понимания задачи. • Более эффективный алгоритм может привести к улучшениям двух видов: • Повышение эффективности поиска путем скорейшего отказа от ненужного перебора и от вычисления бесполезных вариантов. • Применение cтруктур данных, более приспособленных для представления объектов программы, с целью реализовать операции над ними более эффективно.
Предикаты порождения • Мы можем получить все решения для некоторой цели одно за другим с помощью перебора, но при получении нового решения предыдущее становится недоступным. Предикаты порождения позволяют получить доступ ко всем решениям сразу, собирая их в список. • Для получения множества всевозможных решений можно использовать следующий встроенные предикаты: • bagof – все возможные решения, в т.ч. Дубликаты • setof – все возможные решения, отсортированные и без дубликатов • findall– решения без альтернативных конкретизаций • Рассмотрим их подробнее…
Предикат bagof • Предикат bagof(X,P,L) порождает список L всех объектов X, удовлетворяющих цели Р. • X и Р должны содержать общие переменные. Если нет ни одного решения, то согласование цели завершается неудачей. Если один и тот же X найден многократно, то все его экземпляры будут в списке. • Пример: ищем потомков через отношение «предок» • ?- ancestor('Каин', X). • X = 'Алиса' • X = 'Диана' ; • X = 'Мадонна' ; • X = 'Александр' ; • X = 'Эрнест '; • X = 'Альберт' ; • X = 'Марианна' ; • X = 'Кристина' ; • X = 'Марианна' ; • ?- bagof(X, ancestor('Каин', X), L). • L = ['Алиса', 'Диана', 'Мадонна', 'Александр', 'Эрнест', 'Альберт', 'Марианна', 'Барбара', 'Роза'|...] • Если цель будет содержать другие неконкретизированные переменные, то для каждой из возможных конкретизаций этих переменных получается свой список решений: • ?- bagof(X, ancestor(Y, X), L). • Y = 'Адам' • L = ['Каин', 'Авель', 'Сара', 'Алиса', 'Диана', 'Мадонна', 'Александр', 'Эрнест', 'Альберт'|...];
Предикат setof • Предикат setof(X,P,L) работает аналогично bagof, но список L будетупорядочен и не будет содержать повторяющихся элементов. • ?- setof(X, ancestor('Каин', X), L). L = ['Александр', 'Алиса', 'Альберт', 'Барбара', 'Диана', 'Кристина', 'Мадонна', 'Марианна', 'Роза'|...] ; • Убедимся, что нет повторов: применим предикат для печати списка: print_list([X]):- write(X), nl. print_list([X|T]):- write(X), nl, print_list(T). setof(X, ancestor('Каин', X), L), print_list(L). Александр Алиса Альберт Барбара Диана Кристина Мадонна Марианна Роза Филипп Эрнест
Предикат findall • Предикат findall(X, P, L) отличается от предиката bagof тем, что собирает в список L все объекты X, удовлетворяющие цели Р, не обращая внимания на конкретизации тех переменных, которых нет в X. • Если не существует ни одного решения, то цель findall(X,P,L) все равно успешна и L=[ ]. • ?- findall(X, ancestor(Y, X), L). X = _G369 Y = _G368 L = ['Каин', 'Авель', 'Сара', 'Мэри', 'Майкл', 'Алиса', 'Диана', 'Мадонна', 'Барбара'|...] ; No
Выводы • Существует ряд правил, следование которым позволяет сократить количество ошибок в разрабатываемых программах. • Для оценки качества программы существует несколько критериев: правильность, эффективность, простота, читабельность, удобство модификации,документированность • Принцип пошаговой детализации - хороший способ организации процесса разработки программ. Пошаговая детализация применима к отношениям, алгоритмам и структурам данных. • Следующие методы помогают находить идеи для совершенствования программ: • Применение рекурсии: выявить граничные и общие случаи рекурсивного определения. • Обобщение: рассмотреть такую более общую задачу, которую проще решить, чем исходную. • Использование рисунков: графическое представление помогает в выявлении отношений. • Полезно следовать некоторым стилистическим соглашениям для уменьшения опасности внесения ошибок в программы и создания программ, легких для чтения, отладки и модификации. • В пролог-системах обычно имеются средства отладки. Наиболее полезными являются средства трассировки программ. • Следует заботиться об эффективности реализации программ, наряду с их правильностью. • Часто требуется получить множество решений, возвращаемых предикатом, в виде единого списка; для этого удобно воспользоваться предикатами порождения.