Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Теоретический_курс.doc
Скачиваний:
36
Добавлен:
10.11.2019
Размер:
7.68 Mб
Скачать

3.7.3. Метод: процедура или функция?! Метод: процедура или функция?!

Все функции и процедуры в теоретических материалах учебного курса представлены как методы. Но фактически, можно разделить методы на две составные части: функции и процедуры.

Процедура ― это подпрограмма (член класс или структуры, участок кода), которая может вызываться в коде основной программы и производить какие либо действия с переданными в неё переменными.

Функция ― это такая же процедура, только ей свойственно явное возвращение результат её работы (ключевое слово return).

Ниже представлено два метода:

static void CopyString(string a, ref string b)

{

b = a + b;

}

static string ReturnString(string a, string b)

{

return a + b;

}

Из анализа приведённого выше кода видно, что процедура CopyString – не возвращает значений с помощью слова return, то есть напрямую. Она неявно возвращает и изменяет значение по ссылке через ref. Внизу же у нас функция.

Процедура в общем случае:

<модификатор доступа> void <название> (параметры)

{

// Тело процедуры

}

Функция в общем случае:

<модификатор доступа> <тип, кроме void> <название> (параметры)

{

// Тело функции

return <тип>;

}

3.7.4. Метод: рекурсия Метод: рекурсия

В С# допускается, чтобы метод вызывал самого себя. Этот процесс называется рекурсией, а метод, вызывающий самого себя, — рекурсивным. Вообще, рекурсия представляет собой процесс, в ходе которого нечто определяет само себя. В этом отношении она чем-то напоминает циклическое определение. Рекурсивный метод отличается главным образом тем, что он содержит оператор, в котором этот метод вызывает самого себя. Рекурсия является эффективным механизмом управления программой.

Классическим примером рекурсии служит вычисление факториала числа:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace LC_Console

{

class Program

{

// Рекурсивный метод

static int factorial(int i)

{

int result;

if (i == 1)

return 1;

result = factorial(i - 1) * i;

return result;

}

static void Main(string[] args)

{

label1:

Console.WriteLine("Введите число: ");

try

{

int i = int.Parse(Console.ReadLine());

Console.WriteLine("{0}! = {1}", i, factorial(i));

}

catch (FormatException)

{

Console.WriteLine("Неккоректное число");

goto label1;

}

Console.WriteLine("Для продолжения нажмите любую клавишу . . .");

Console.ReadKey();

}

}

}

}

/* Выведет:

* Введите число:

* :3

* 3! = 6

* Для продолжения нажмите любую клавишу . . .

*/

Обратим внимание, что рекурсивный метод factorial вызывает сам себя, при этом переменная i с каждым вызовом уменьшается на 1.

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

Главное преимущество рекурсии заключается в том, что она позволяет реализовать некоторые алгоритмы яснее и проще, чем итерационным способом. Например, алгоритм быстрой сортировки довольно трудно реализовать итерационным способом. А некоторые задачи, например искусственного интеллекта, очевидно, требуют именно рекурсивного решения.

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