Циклы, управляемые отказом

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

Простым примером цикла, управляемого отказом, служит вопрос Goal, write (Goal), nl, fail?, который приводит к выводу всех решений цели goal на экран. Такой цикл использован в оболочках, описанных в программах 12.6 и 12.7.

Цикл, управляемый отказом, может быть использован при определении системного предиката tab(N), предназначенного для вывода на экран N пробелов. В нем используется отношение between, описанное в программе 8.5:

tab (N)¬ between (1,N,I), put(32), fail.

Каждая интерактивная программа предыдущего раздела может быть переписана с использованием циклов, управляемых отказом. Новый вариант основного интерактивного цикла приведен в программе 12.8. Он основан на незавершающемся системном предикате repeat, который может быть задан с помощью минимальной рекурсивной процедуры, приведенной в программе 12.8. В отличие от программы 12.4 в данной программе решение цели echo(X) приводит к безуспешному вычислению, если только Х не символ конца файла. Безуспешное вычисление вызывает возврат к цели repeat, цель выполняется, считывается и затем выводится на экран следующий терм. Отсечение в определении предиката echo гарантирует от позднейшего повторения цикла repeat.

 

echo ¬repeat, read(X), echo(X),!.

echo(X) ¬end_of_file(Х),!.

echo(X) ¬write(X), nl, fail.

repeat.

repeat¬ repeat.

Программа 12.8. Основной интерактивный цикл repeat.

 

Цикл, управляемый отказом и использующий предикат repeat, имеет название repeat-цикл.Такие циклы аналогичны циклам вида repeat в обычных языках программирования. Repeat-циклы применяются в Прологе для описания интерактивного взаимодействия с внешней системой путем повторяющегося ввода и (или) вывода. В repeat-цикле обязательно должен присутствовать предикат, гарантированно приводящий к безуспешным вычислениям (в программе 12.8 это – цель echo(Х)), определяющим продолжение итерации. Такой предикат вычисляется успешно лишь в момент выхода из цикла. Можно сформулировать полезное эвристическое правило построения repeat-циклов: в теле правила, содержащего цель repeat, должно быть отсечение, предохраняющее от незавершающихся вычислений при возврате в цикл.

Мы используем repeat-цикл для определения системного предиката consult(File), предназначенного для чтения и последующего добавления к программе предложений из некоторого файла. Определение приведено в программе 12.9. Системные предикаты see(File) и seen используются соответственно для открытия и закрытия входного файла.

 

consult(File)¬ see(File), consult_loop, seen

consult coop ¬

repeat, read(Clause), process(Clause),!.

process (X) ¬

end_of_file(X),!. process(Clause) ¬

assert (Clause), fail,

Программа 12.9. Обращение к файлу.

 

Рекурсивные циклы предпочтительнее repeat-циклов, поскольку последние не имеют логической интерпретации. На практике, однако, repeat-циклы часто необходимы при выполнении большого объема вычислений, особенно в реализациях Пролога без оптимизации остатка рекурсии и (или) без сборки мусора. Обычно явный отказ приводит к требованию некоторого зависящего от реализации объема памяти.