Программирование на языке пролог
Шрифт:
присоединить([с],[b],Y)
На этом этапе текущая цель и ее предшественники могут быть представлены в следующем виде:
oбp([a,b,c,d],_46) (цель A)
oбp([b,c,d],[d|_50]) (цель B)
присоединить([d,с],[b],[d|_51])
присоединить([с],[b],_52)
Прежде чем читать дальше, вам следует убедиться в том, что вы понимаете, почему это предшественники данной цели, а также почему у нее нет никаких других предшественников. С приведенным здесь изображением предшественников связана одна особенность,
Глядя на предшественников текущей цели можно получить ясное представление о том, что происходит с программой и почему она делает то, что наблюдается. Одной из команд, которые пользователю разрешается вводить при наступлении управляемого события для некоторой цели может быть выдача сведений о каких-либо из ее предшественников. Таким образом, если вы чувствуете, что ваша программа тратит где-то много времени и подозреваете, что это может быть результатом зацикливания, то верная стратегия состоит в том, чтобы прервать выполнение, включить полную трассировку, а затем посмотреть на предшественников, чтобы понять, где вы находитесь.
Другой набор команд, которыми можно воспользоваться при наступлении управляемого события, связан с изменением желаемого объема трассировки. Некоторые из более грубых управляющих воздействий состоят в следующем.
• Удаление всех контрольных точек. Это имеет тот же эффект, что и вызов цели nodebug(см. разд. 6.13).
• Отключение полной трассировки. Это имеет тот же эффект, что и вызов цели notrace(см. разд. 6.13).
• Включение полной трассировки. Это имеет тот же эффект, что и вызов цели trace(см. разд. 6.13).
При задании любой из этих команд ваша программа продолжит затем выполнение в указанном режиме до тех пор, пока не дойдет до цели, которую вы намеревались прослеживать. В некоторых версиях Пролога могут быть предусмотрены более тонкие средства управления трассировкой. Эти средства помогут вам быстро пройти через те участки выполнения программы, которые не представляют интереса с тем, чтобы сосредоточиться на участках, где, вероятно, имеются ошибки. Здесь возможны следующие команды:
• «creep» (ползти): Продолжить выполнение программы с полной трассировкой до тех пор, пока не поступит новое указание (при наступлении следующего управляемого события).
• «skip» (пропустить): Продолжить выполнение программы без выдачи какой-бы то ни было трассировочной информации до тех пор, пока не наступит какое-либо событие, относящееся к текущей цели.
• «leap» (перескочить); Продолжить выполнение программы без выдачи трассировочной информации до тех пор, пока либо не будет достигнута контрольная точка, либо не наступит событие, относящееся к текущей цели.
Первая из этих команд соответствует случаю, когда вы хотите, начиная с данного момента, тщательно проследить за ходом выполнения программы. Вторая команда соответствует случаю, когда вас не интересует ход согласования какой-либо цели,
наивсорт(Х,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), вы также можете попросить о выборе альтернативы. Таким образом, если известно, что первый найденный ответ не позволяет успешно завершить остальную часть программы, можно тут же попросить о поиске другого решения. Это означает, что вы сможете быстрее добраться до той части программы, где содержится ошибка. Без такого режима вам пришлось бы «караулить» возможную неудачу процесса согласования после того, как была найдена первая альтернатива.