Добавить в цитаты Настройки чтения

Страница 111 из 502

<b>    Console.WriteLine(&quot;Факториал числа 5 равен &quot; + f.FactR(5));</b>

<b>    Console.WriteLine();</b>

<b>    Console.WriteLine(&quot;Факториалы, рассчитанные итерационным методом.&quot;);</b>

<b>    Console.WriteLine(&quot;Факториал числа 3 равен &quot; + f.FactR(3));</b>

<b>    Console.WriteLine(&quot;Факториал числа 4 равен &quot; + f.FactR(4));</b>

<b>    Console.WriteLine(&quot;Факториал числа 5 равен &quot; + f.FactR(5));</b>

<b>  }</b>

<b>}</b>

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

<b>Факториалы, рассчитанные рекурсивным методом.</b>

<b>Факториал числа 3 равен 6 </b>

<b>Факториал числа 4 равен 24 </b>

<b>Факториал числа 5 равен 120</b>

<b>Факториалы, рассчитанные итерационным методом.</b>

<b>Факториал числа 3 равен 6 </b>

<b>Факториал числа 4 равен 24 </b>

<b>Факториал числа 5 равен 120</b>

Принцип действия нерекурсивного метода FactI() вполне очевиден. В нем используется цикл, в котором числа, начиная с 1, последовательно умножаются друг на друга, постепенно образуя произведение, дающее факториал.

А рекурсивный метод FactR() действует по более сложному принципу. Если метод FactR() вызывается с аргументом 1, то он возвращает значение 1. В противном случае он возвращает произведение FactR(n-1)*n. Для вычисления этого произведения метод FactR() вызывается с аргументом n-1. Этот процесс повторяется до тех пор, пока значение аргумента n не станет равным 1, после чего из предыдущих вызовов данного метода начнут возвращаться полученные значения. Например, когда вычисляется факториал числа 2, то при первом вызове метода FactR() происходит второй его вызов с аргументом 1. Из этого вызова возвращается значение 1, которое затем умножается на 2 (первоначальное значение аргумента n). В итоге возвращается результат 2, равный факториалу числа 2 (1x2). Было бы любопытно ввести в метод FactR() операторы, содержащие вызовы метода WriteLine(), чтобы наглядно показать уровень рекурсии при каждом вызове метода FactR(), а также вывести промежуточные результаты вычисления факториала заданного числа.

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

Ниже приведен еще один пример рекурсии для вывода символьной строки в обратном порядке. Эта строка задается в качестве аргумента рекурсивного метода DisplayRev().

<b>// Вывести символьную строку в обратном порядке, используя рекурсию.</b>

<b>using System;</b>

<b>class RevStr {</b>

<b>  // Вывести символьную строку в обратном порядке. </b>

<b>  public void DisplayRev(string str) { </b>

<b>    if (str.Length &gt; 0)</b>

<b>      DisplayRev(str.Substring(1, str.Length - 1)); </b>

<b>    else</b>

<b>      return;</b>

<b>    Console.Write(str[0]);</b>

<b>  }</b>

<b>}</b>

<b>class RevStrDemo { </b>

<b>  static void Main() {</b>

<b>    string s = &quot;Это тест&quot;;</b>

RevStr rsOb = new RevStr();

<b>    Console.WriteLine(&quot;Исходная строка: &quot; + s);</b>

<b>    Console.Write(&quot;Перевернутая строка: &quot;); </b>

<b>    rsOb.DisplayRev(s);</b>

<b>    Console.WriteLine();</b>

<b>  }</b>

<b>}</b>

Вот к какому результату приводит выполнение этого кода.

<b>Исходная строка: Это тест </b>

<b>Перевернутая строка: тсет отЭ</b>

Всякий раз, когда вызывается метод DisplayRev(), в нем происходит проверка длины символьной строки, представленной аргументом str. Если длина строки не равна нулю, то метод DisplayRev() вызывается рекурсивно с новой строкой, которая меньше исходной строки на один символ. Этот процесс повторяется до тех пор, пока данному методу не будет передана строка нулевой длины. После этого начнется раскручиваться в обратном порядке механизм всех рекурсивных вызовов метода DisplayRev(). При возврате из каждого такого вызова выводится первый символ строки, представленной аргументом stг, а в итоге вся строка выводится в обратном порядке.

Рекурсивные варианты многих процедур могут выполняться немного медленнее, чем их итерационные эквиваленты из-за дополнительных затрат системных ресурсов на неоднократные вызовы метода. Если же таких вызовов окажется слишком много, то в конечном итоге может быть переполнен системный стек. А поскольку параметры и локальные переменные рекурсивного метода хранятся в системном стеке и при каждом новом вызове этого метода создается их новая копия, то в какой-то момент стек может оказаться исчерпанным. В этом случае возникает исключительная ситуация, и общеязыковая исполняющая среда (CLR) генерирует соответствующее исключение. Но беспокоиться об этом придется лишь в том случае, если рекурсивная процедура выполняется неправильно.