Скачиваний:
45
Добавлен:
22.05.2015
Размер:
164 Кб
Скачать
☆

Функциональный стиль предикатов

Одной из отличительных особенностей Visual Prolog 7 от ISO‑Прологов является то, что предикаты можно использовать в качестве функций. Здесь следует отметить, что любой предикат Пролога имеет своё значение (истина или ложь) в области истинности. Однако только у предикатов, объявленных как функции, кроме области истинности есть ещё область значений, подобная той, какая есть у математических функций. И эти области не надо путать и смешивать.

Если проводить аналогию с императивными языками, то можно сказать, что предикаты подобны процедурам, а предикаты-функции подобны функциям императивных языков. Эта аналогия не совсем точна, но, во всяком случае, показывает отличие предикатов от предикатов-функций.

Пример 3. Модифицируем рассмотренный в примере 1 предикат ряд/2, так, чтобы он приобрёл функциональный стиль. Этот предикат должен быть недетерминированным и возвращать очередное значение Value члена арифметической прогрессии, начиная с начального члена From, до конечного члена UpTo, с шагом равным +1. Объявим такой предикат в виде функции:

class predicates

ряд : (integer From, integer UpTo) -> integer Value nondeterm.

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

Опишем этот рекурсивный предикат-функцию в виде:

clauses

ряд(N,K) = N :- N<=K. % условие продолжения рекурсии

ряд(N,K) = ряд(N+1,K) :- N<K. % рекурсия с проверкой

Первое предложение содержит условие продолжения и окончания рекурсии N<=K и возвращаемое значение N. Отсечение мы здесь не ставим, так как при его наличии мы сможет получить только один член арифметической прогрессии – первый.

Второе предложение также содержит условие продолжения рекурсии N<K и рекурсивный вызов функции ряд(N+1,K).

Исходный текст консольного приложения выглядит так:

implement main

    open core, console

constants

className = "com/visual-prolog/main".

classVersion = "$JustDate: $$Revision: $".

class predicates

ряд : (integer From, integer UpTo) -> integer Value nondeterm.

clauses classInfo(className, classVersion).

ряд(N,K) = N :- N<=K. ряд(N,K) = ряд(N+1,K) :- N<K.

run():-    init(),    write(ряд(4,8)," "),    fail;    write("\nПрограмма завершена"),

   _=readchar().

end implement main

goal

   mainExe::run(main::run).

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

Пример 4. Давайте посмотрим, как можно разворачивать бесконечный ряд чисел, указав только первый член ряда. Для этого удалим все упоминания в программе о конечном члене ряда UpTo. А также для задержки бесконечного цикла на каждом витке, добавим в цель run() ожидание ввода с клавиатуры перед предикатом принудительного отката назад:

implement main

    open core, console

constants className = "com/visual-prolog/main". classVersion = "$JustDate: $$Revision: $".

class predicates ряд : (integer From) -> integer Value multi.

clauses classInfo(className, classVersion).

ряд(N) = N. ряд(N) = ряд(N+1).

run():-    init(),    write(ряд(-2)),    _=readchar(),    fail;    write("\nПрограмма завершена"),    _=readchar().

end implement main

goal

   mainExe::run(main::run).

Если перед предикатом принудительного отката назад не делать ожидание ввода с клавиатуры, то программа будет выводить все числа до максимального числа из домена integer .

Пример 5. Разработаем рекурсивный предикат геом/3, в функциональном стиле, который возвращает при откате назад заданное количество Count членов арифметической прогрессии, начиная с начального значения From и с шагом +2. Объявим предикат-функцию геом/2 с нижеуказанными доменами:

class predicates

ряд : (integer From, integer Count) -> integer Value nondeterm.

Опишем предложения предиката:

ряд(N,0) = N :- !.

ряд(N,Count) = N :- Count>0.

ряд(N,Count) = ряд(N+1,Count-1) :- Count>0.

Первое предложение предиката содержит условие останова рекурсии Count=0 и возвращаемое значение функции N.

Второе предложение содержит условие продолжения рекурсии Count>0 и возвращаемое значение функции N.

Третье предложение содержит условие продолжения рекурсии Count>0 и собственно рекурсивный вызов функции.

Исходный текст консольного приложения выглядит так:

implement main

    open core, console

constants

className = "com/visual-prolog/main".

classVersion = "$JustDate: $$Revision: $".

class predicates

ряд : (integer From, integer Count) -> integer Value nondeterm.

clauses classInfo(className, classVersion).

ряд(N,0) = N :- !.

ряд(N,Count) = N :- Count>0.

ряд(N,Count) = ряд(N+1,Count-1) :- Count>0.

run():-    init(),    write(ряд(1,8)," "),    fail;    write("\nПрограмма завершена"),    _=readchar().

end implement main

goal

   mainExe::run(main::run).

Задание 4. Написать предикат вычисления факториала.

Задание 5. Написать предикат-функцию вычисления факториала.

Задание 6. Написать предикат вычисления n-го члена ряда Фибоначчи. Подсказка: n-й член ряда Фибоначчи равен сумме: fib(n) = fib(n‑1)+fib(n‑2). Нулевой и первый члены ряда равны единице: fib(0) = 1, fib(1) = 1.

Задание 7. Написать предикат-функцию вычисления n-го члена ряда Фибоначчи.

Задание 8. Написать предикат-функцию для вычисления функции Аккермана. Эта функция часто используется для оценки функциональных языков. Подсказка: функция Аккермана имеет следующее математическое описание:

ack(0, x) = x+1

ack(x, 0) = ack(x-1, 1)

ack(x, y) = ack(x-1, ack(x,y-1))

Проверьте с помощью Вашего предиката значения функции Аккермана для следующих пар аргументов:

ack(3,6,) = 509

ack(3,7,) = 1021

ack(3,8,) = 2045

ack(3,9,) = 4093

ack(3,10,) = 8189

Задание повышенной сложности 1. Написать предикат, который будет истинным, если вводимое с клавиатуры число является простым. Подсказка: число является простым, если это число делится с остатком на все меньшие числа вплоть до двойки. Операция вычисления остатка от деления называется div. Например, Остаток = 7 div 3. В данном случае остаток равен единице.

Задание повышенной сложности 2. Написать предикат, который вычисляет наибольший общий делитель (НОД) двух чисел, вводимых с клавиатуры. Например, числа 8 и 12 имеют НОД, который равен 4, так как и 8 и 12 делятся без остатка на 4. Подсказка: Для этого примера надо проверить числа от 8 до 1 на роль наибольшего общего делителя.

Соседние файлы в папке Лабораторные работы