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

Сумма ряда

Для начала рассмотрим пример суммирования первых n членов арифметической прогрессии. Хотя такую сумму можно легко вычислить с помощью конечной формулы, мы всё же посмотрим, как это будет выглядеть на Прологе в виде рекурсии.

Пример 6. Для вычисления суммы объявим предикат sum/3, в котором входными аргументами является первый член ряда From и количество суммируемых членов Count. Третий аргумент Summa является выходным. Шаг рекурсии для простоты равен 1.

class predicates

sum : (integer From, unsigned Count, integer Summa) procedure (i,i,o).

Рекурсивный предикат будет содержать два предложения:

clauses

sum(_,0,0):- !.

sum(V,Count,Summa) :- sum(V+1,Count-1,Summa1), Summa=Summa1+V.

Первое предложение является условием окончания рекурсии и содержит условие Count=0, для которого значение суммы равно нулю Summa=0. Это означает, что если ряд не содержит членов, то его сумма равна нулю.

Второе предложение собственно суммирует значения членов ряда с помощью предиката Summa=Summa1+V. Этот предикат можно выразить словами так: “сумма n членов ряда равна сумме n-1 членов ряда плюс n-й член ряда”. Основной особенностью такой рекурсии является то, что суммирование производится после рекурсивного вызова, другими словами – на выходе из рекурсии.

Программа имеет следующий код:

implement main

    open core, console

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

class predicates sum : (integer From, unsigned Count, integer Summa) procedure (i,i,o).

clauses classInfo(className, classVersion).

sum(_,0,0):- !. sum(V,Count,Summa) :- sum(V+1,Count-1,Summa1), Summa=Summa1+V.

run():-    init(),    sum(1,3,Summa),    write(Summa),    write("\nПрограмма завершена"),    _=readchar().

end implement main

goal

   mainExe::run(main::run).

Запустив программу можно увидеть ответ, равный шести. Действительно, сумма первых трёх членов ряда 1+2+3 равна шести. Замечание: предикат sum/3 в этом примере можно записать более лаконично:

sum(_,0,0):- !.

sum(V,Count,Summa1+V) :- sum(V+1,Count-1,Summa1).

Однако в такой записи не очевидно то, что вычисления производятся на выходе из рекурсии.

Задание 9. Модифицировать предикат из примера 6 в предикат-функцию со следующим объявлением:

class predicates

sum : (integer From, unsigned Count, ) -> integer Summa procedure (i,i,o).

Пример 7. Суммирование значений членов ряда можно производить и перед рекурсивным вызовом, другими словами – на входе в рекурсию. Для этого в предикат sum/3 надо добавить ещё один входной аргумент Temp, в котором мы будем сохранять текущую сумму. Назовём новый предикат sum1/4 и объявим его так:

class predicates

sum1 : (integer From, unsigned Count, integer Temp, integer Summa) procedure (i,i,i,o).

При вызове этого предиката аргумент Temp должен быть равен нулю, так как он является текущей суммой.

sum1(_,0,Summa,Summa) :- !.

sum1(N,Count,Temp,Summa) :-

sum1(N+1,Count-1,Temp+N,Summa).

Полный текст программы выглядит так:

implement main

    open core, console

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

class predicates

sum1 : (integer From, unsigned Count, integer Temp, integer Summa) procedure (i,i,i,o).

clauses

classInfo(className, classVersion).

sum1(_,0,Summa,Summa) :- !.

sum1(N,Count,Temp,Summa) :-

sum1(N+1,Count-1,Temp+N,Summa).

run():-    init(),

   sum1(1,3,0,Summa1),write("Ответ=",Summa1),nl,

   write("\nПрограмма завершена"),

   _=readchar().

end implement main

goal

   mainExe::run(main::run).

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

Пример 8. Этот пример демонстрирует различия между двумя способами вычисления суммы ряда: с одной стороны сумму можно подсчитать на выходе из рекурсии, как это сделано в примере 6; с другой стороны – на входе в рекурсию, как это сделано в примере 7. Второй способ расходует стек немного быстрее, чем первый. Для демонстрации размера используемой памяти применяется предикат memory::getUsedStack(), который возвращает размер используемого стека. Вызывая этот предикат перед рекурсивным вызовом и после рекурсивного вызова можно наглядно увидеть расход памяти на каждом витке цикла:

implement main

    open core, console

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

class predicates sum : (integer From, unsigned Count, integer Summa) procedure (i,i,o). sum1 : (integer From, unsigned Count, integer Temp, integer Summa) procedure (i,i,i,o).

clauses classInfo(className, classVersion).

sum(_,0,0) :- write("Дно Stack=",memory::getUsedStack(),"   Summa=0"), nl,!. sum(V,Count,Summa1+V) :-

   write("Вызов Stack=",memory::getUsedStack()),nl,    sum(V+1,Count-1,Summa1),    writef("Возврат Stack=%   Summa=%+%=%", memory::getUsedStack(),Summa1,V,Summa1+V),nl.

sum1(_,0,Summa,Summa) :-

   write("Дно Stack=",memory::getUsedStack(), "   Summa=",Summa),nl,!. sum1(N,Count,Temp,Summa) :-

   writef("Вызов Stack=%   Summa=%+%=%", memory::getUsedStack(),Temp,N,Temp+N),nl,    sum1(N+1,Count-1,Temp+N,Summa),    write("Возврат Stack=",memory::getUsedStack()),nl.

run():-    init(),    sum(1,3,Summa),write("Ответ=",Summa),nl,nl,    write("------------------------------"),nl,nl,    sum1(1,3,0,Summa1),write("Ответ=",Summa1),nl,     write("\nПрограмма завершена"),    _=readchar().

end implement main

goal

   mainExe::run(main::run).

Результаты работы этой программы демонстрируют различия между двумя способами рекурсий. Предикат sum/3 демонстрирует вычисления на выходе из рекурсии, а предикат sum1/4 – перед входом в рекурсию.

Задание 10. Разработать программу, вычисляющую сумму первых пяти случайных чисел, возвращаемых функцией Random=math::random(Max). Подсказка: 0 <= Random < Max.

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