50
3.2. К
ОГДА РЕКУРСИЯ НЕОБХОДИМА
Далеко не всегда рекурсия бывает необходимой. Напомним,
что при
каждом рекурсивном вызове функции выделяется память для размещения ее
переменных. Кроме этих локальных переменных нужно еще сохранять теку-
щее состояние вычислений,
чтобы вернуться к нему, когда закончится вы-
полнение новой активации функции и нужно будет вернуться к старой. Все
эти данные, а также и адрес возврата операционная система помещает в сис-
темный стек. Эти накладные расходы необходимо учитывать при выборе ме-
жду рекурсивным и нерекурсивным решением.
Для примера рассмотрим рекурсивную
функцию вычисления факто-
риала. Определение факториала по своей сути является рекурсивным, и, ка-
залось бы, алгоритм его вычисления тоже должен быть таким же. Приведен-
ная ниже функция
Factorial является калькой определения:
Листинг 3.2. Вычисление факториала. Вариант 1
static int Factorial(int k)
{
int result;
if (k == 1)
result = 1;
else
result = k * Factorial(k-1);
return result;
}
Однако итеративный алгоритм вычисления факториала нисколько не
сложнее, и к тому же лишен накладных расходов, связанных с рекурсией:
Листинг 3.3. Вычисление факториала. Вариант 2
static int FactorialI(int k)
{
int result = 1;
for (int i = 1; i <= k; i++)
result *= i;
return result;
}
4 / 23
51
На ряде других аналогичных примеров можно показать, что если име-
ется
очевидное итеративное решение задачи, то следует избегать рекурсии.
Любую рекурсивную программу можно
преобразовать в чисто итера-
тивную. Но для этого придется самим создавать стек для рекурсивных вызо-
вов и оперировать с ним, и порой эти операции до такой степени заслоняют
основной алгоритм, что понять его становится весьма и весьма трудно. По-
этому алгоритмы, которые по своей природе скорее рекурсивны, чем итера-
тивны, следует представлять в виде рекурсивных процедур.
Do'stlaringiz bilan baham: