3668

Процедури і функції — методи класу

Лекция

Информатика, кибернетика и программирование

Процедури і функції — методи класу Історично першим способом структуризації програм в мовах програмування високого рівня було використання процедур і функцій — щодо самостійних фрагментів програм, оформлених особливим чином і забезпечених ...

Украинкский

2012-11-05

64 KB

2 чел.

Процедури і функції — методи класу

Історично першим способом структуризації програм в мовах програмування високого рівня було використання процедур і функцій — щодо самостійних фрагментів програм, оформлених особливим чином і забезпечених ім'ям. Згадка цього імені в програмі називається викликом процедури (функції).

Відмінність функції від процедури полягає в тому, що результатом виконання операторів, утворюючих тіло функції, завжди є значення деякого типу, тому функція може брати участь в утворенні виразів разом із змінними і константами. Умовимося надалі називати процедури і функції загальним словом підпрограми, якщо тільки це дозволяє висловлюваний матеріал.

Структуризація програм необхідна по двох причинах. По-перше, неможливо написати більш менш складну програму як єдине ціле, не розчленовувавши її на окремих відносно самостійні частини — підпрограми. По-друге, підпрограми відкривають можливість повторного використання одного разу створеного коду і сприяють появі бібліотек підпрограм. У мові С# підпрограми не мають самостійного значення — вони можуть бути тільки методами класу.

Опис підпрограм

У С# немає спеціальних зарезервованих слів procedure і function для опису підпрограм. Оскільки вони є методами класу, ці слова надмірні. Синтаксис опису такий:

[модифікатори]   <Тип>  <Ім'я>   ([Формальниє_параметри>]) {<Тіло>}

У квадратних дужках вказані необов'язкові елементи. Модифікатори визначають область видимості підпрограми і детально розглядаються в розділі І..

Зараз поясню лише два модифікатори — private і public. Будь-які члени класу (зокрема методи-підпрограми), оголошені з модифікатором private, доступні тільки в методах даного класу. Модифікатор public робить метод (підпрограму) доступним в будь-якому місці програми. Якщо модифікатор не вказаний, вважається, що даний член класу помічений як закритий (з модифікатором private).

Формальні параметри можуть бути відсутніми, але і в цьому випадку круглі дужки за ім'ям підпрограми обов'язкові.

Тип підпрограми може бути будь-яким типом даних. В цьому випадку підпрограма є функцією, яка повертає результат вказаного типу. У тілі функції обов'язково указується оператор return, який привласнює функції потрібне значення. Як тип можна вказати зарезервоване слово void, яке означає відсутність типу. В цьому випадку підпрограма є процедурою і в ній не можна указувати оператора return.

Ім'я підпрограми повинне бути унікальним в поточній області видимості ідентифікатора.

Тіло підпрограми обов'язково реалізується у вигляді блоку операторів, тому за закриваючою круглою дужкою повинна слідувати відкриваюча фігурна, навіть якщо тіло підпрограми відсутній або містить єдиний оператор.

Приклади описів:

int A()  {...}

void В(...) {...}

public string С() { . . . }

Тут А і В — закриті члени класу, які доступні тільки в методах цього ж класу, причому А — цілочисельна функція, а В — процедура. С — відкрита функція рядкового типу, доступна в будь-якій точці програми.

Формальні параметри

Формальні параметри є засобом настройки підпрограми на виконання потрібної роботи. Наприклад, звичайна математична функція sin(х) має формальний параметр х, в тілі функції що трактує як кут, для якого слід обчислити значення синуса.

У С# розрізняють три різновиди (статусу) формальних параметрів: вхідні, вихідні і посилальні. За допомогою вхідних параметрів програма передає дані в тіло підпрограми. При цьому, фактично, в підпрограму передаються не дані, а їх копії, тому зміна вхідних параметрів в підпрограмі ніяк не передається програмі що визиває. Вихідні параметри оголошуються із зарезервованим словом out. Вони призначені для передачі даних з підпрограми в програму що визиває. У тілі підпрограми обов'язково повинен бути присутнім оператор привласнення цим параметрам нового значення — за цим стежить компілятор. Посилальні параметри передаються підпрограмі по посиланню, тобто в тіло підпрограми передається адреса параметра в пам’яті комп'ютера. Такі параметри позначаються зарезервованим словом ref. Вони дозволяють як передати дані в підпрограму, так і отримати з неї нові дані.

При виборі того або іншого різновиду формальних параметрів потрібно керуватися наступними міркуваннями:

  •  Якщо параметр визначений як вхідний, програміст може бути упевнений в тому, що виклик підпрограми не буде пов'язаний з яким-небудь побічним ефектом, оскільки в підпрограму передається не сам параметр, а лише його копія. На місці цього параметра може розташовуватися довільний вираз відповідного типу. Проте програма витрачатиме додаткові ресурси комп'ютера (пам'ять і час), особливо якщо в підпрограму передається великий масив даних.
  •  Якщо параметр помічений як вихідний (out) або посилальний (ref), в підпрограму передається сам параметр (його адреса), а не його копія, що мінімізує витрати пам'яті і часу в точці виклику. Він може (ref) або повинен (out) змінитися в підпрограмі, тому на його місці слід указувати не вираз, а змінну відповідного типу.
  •  У підпрограмі можна оголосити не один, а декілька параметрів. В цьому випадку сусідні параметри розділяються комами, утворюючи список параметрів Останнім (або єдиним) параметром списку може оголошуватися масив будь-якого типу із зарезервованим словом params. В цьому випадку на місці даного параметра при виклику підпрограми може стояти скільки завгодно параметрів такого типу.

Програма з лістингу 19.1 ілюструє використання функції А з довільною кількістю формальних параметрів.

Лістинг 18.1. Функція з довільною кількістю параметрів

using System;

class Program

{

   static int A(params int[] I)

   // Ця функція знаходить суму довільної кількості  цілочисельних параметрів звернення

   {

       int Sum = 0;

       for (int i = 0; i < I.Length; i++)

           Sum += I[i]; ;

       return Sum;

   }

   static void Main()

   {

       Console.WriteLine("{0} {1}", A(1, 2, 3), A(4 , 5, 6, 7, 8, 9));

       Console.ReadLine();

   }

}

Виклик підпрограм

Для виклику підпрограми потрібно вказати її ім'я і список фактичних параметрів (якщо в описі підпрограми є непорожній список формальних параметрів). Список фактичних параметрів повинен відповідати за типом, кількістю, порядком проходження і статусом списку формальних параметрів.

Якщо в списку п формальних параметрів, то фактичних параметрів повинно бути не менше п (відповідність по кількості). Кожному і-му формальному параметру ставиться у відповідність і-й фактичний параметр. Останньому формальному параметру за умови, що він оголошений із зарезервованим словом params, ставляться у відповідність фактичні параметри, що все залишилися (відповідність по порядку).

Якщо формальний параметр оголошений із зарезервованим словом ref або out, то фактичний параметр повинен супроводжуватися таким же зарезервованим словом в точці виклику (відповідність по статусу).

Відповідність за типом означає, що фактичний параметр повинен мати такий же (або сумісний з ним) тип, який має формальний параметр.

Відповідність по статусу, тобто поява зарезервованих слів ref і out в точці виклику підпрограми, особливість мови С#, що відрізняє його від всіх інших мов програмування. Цю особливість слід всіляко вітати, оскільки вона нагадує програмістові про те, що даний фактичний параметр є вихідним і його значення може змінитися (у разі ref) або напевно зміниться (у разі out) після виклику підпрограми. Проте із-за незвичності синтаксису ці слова часто забувають указувати, що приводить до синтаксичних помилок, що супроводжуються повідомленнями типу «Argument N: cannot convert from 'Type' to 'Out Type'» («Параметр N: неможливо перетворити 'Type' в 'Out Type'»).

Функції з побічним ефектом

Функція називається функцією з побічним ефектом, якщо крім результату, що обчислюється функцією і що повертається нею в операторові return, вона має вихідні параметри із зарезервованими словами ref або out.

Деяким програмістам, можливо, такі функції сподобаються, проте хороший стиль програмування не рекомендує їх використання. Річ у тому, що функція може викликатися не тільки в операторові привласнення, але і у виразах. Функція з побічним ефектом може порушити комутативність операцій.

Наприклад, якщо int f (out int а) — функція з побічним ефектом, то вираз а + f (out а) в загальному випадку не рівно виразу f (out а) + а.

Якщо по яких-небудь причинах функція повинна повертати декілька значень, логічніше оформити її у вигляді процедури з відповідним набором вихідних параметрів.

Рекурсія

Рекурсія — це такий спосіб організації обчислювального процесу, при якому підпрограма в ході виконання складових її операторів звертається сама до себе.

Розглянемо класичний приклад — обчислення факторіалу. Програма з лістингу 18.2  читає ціле число N і виводить на екран значення N!, яке обчислюється за допомогою рекурсивної функції Fac().

Лістинг 18.2. Обчислення факторіалу за допомогою рекурсивної функції

using System;

class Factorial 

{

static long Fac(int N)     // Рекурсивна функція

{

   if (N <= 1) // Рішення тривіальне?

       return 1; // -Да. Вихід з рекурсії

   else // -Нет. Рекурсія

       return N * Fac(N - 1);

}

static void Main()

{

   Console.WriteLine("Обчислення факториала\n");

Console.Write("Введіть ціле число: ");

string S = Console.ReadLine();

int N = int.Parse(S);

Console.WriteLine("{0}! = {1:n}", N, Fac(N));

Console.ReadLine();

}

}

При виконанні правильно організованої рекурсивної підпрограми здійснюється багатократний перехід від деякого поточного рівня організації алгоритму до нижнього рівня послідовно до тих пір, поки не буде нарешті отримано тривіальне рішення поставленої задачі. У нашому випадку рішення при N = 1 тривіально (факторіал 1 рівний 1) і використовується для зупинки рекурсії.

Рекурсивні алгоритми зазвичай виглядають витонченішими, ніж нерекурсивні, але вони мають два істотні недоліки: по-перше, при великій глибині вкладення рекурсій може виникнути переповнювання стека, оскільки кожен рекурсивний виклик залишає в стеку параметри виклику; по-друге, при вході в рекурсивну підпрограму і при виході з неї витрачається додатковий час для заштовхування параметрів виклику в стек і для витягання їх із стека. У зв'язку з цим рекомендується, якщо це можливо, використовувати нерекурсивні («плоскі») алгоритми. Наприклад, обчислити факторіал можна і за допомогою такої нерекурсивної функции:

static long Fac(int N)

{

long Res = 1;

for (int i = 1; i <= N; i++) Res = Res * i;

return Res;

}

Проте в деяких випадках обійтися без рекурсії неможливо. Наприклад, мені так і не вдалося придумати «плоский» алгоритм обходу всіх гілок дерева каталогів.

При реалізації рекурсивного алгоритму слід по можливості мінімізувати розмір даних, що передаються в рекурсивному виклику.

Лістинг 18.3. Структурізація програми з допомогою процедур

Приклад 1.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

//

   class Program

   {//все методы должны быть объявлены в каком либо классе

       //методы, имеющие обозначение static не обязаны быть привязаны к какому либо объекту

       static void Procedure1()

       {

           Console.WriteLine("Hello from Procedure 1");

       }

       static void Procedure2()

       {

           Console.WriteLine("Hello from Procedure 2");

       }

       static int Procedure3(int i)

       {

           return (int)Math.Pow(i,2);

       }

       static string Procedure4()

       {

           return "Hello World (from Procedure 4)";

       }

       static void Main(string[] args)

       {

           Procedure1();

           Procedure2();

           int result1 = Procedure3(7);

           string result2 = Procedure4();

           Console.WriteLine("результат метода Procedure3:"+result1);

           Console.WriteLine("результат метода Procedure4:"+result2);

       }

   }

 

Приклад 2.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

class A

{

   public void Procedure1()

   {

       Console.WriteLine("Hello from Procedure 1");

   }

   public void Procedure2()

   {

       Console.WriteLine("Hello from Procedure 2");

   }

   public int Procedure3(int i)

   {

       return (int)Math.Pow(i, 2);

   }

   public string Procedure4()

   {

       return "Hello World (from Procedure 4)";

   }

   public static void StaticProcedure()

   {

       Console.WriteLine("Hello from static procedure");

   }

}

   class Program

   {

       static void Main(string[] args)

       {//для использования методов класса А необходимо создать объект даного класса

           A objA = new A();

           objA.Procedure1();

           objA.Procedure2();

           int result1=objA.Procedure3(7);

           string result2 = objA.Procedure4();

           Console.WriteLine("Результат выполнения метода Procedure3:"+result1);

           Console.WriteLine("Результат выполнения метода Procedure4:" + result2);

           //также остаеться возможность использовать статические (static) методы даного класса А

           //для этого необходимо указать какому классу принадлежит статический метод

           //в прошлом примере этого не требовалось, поскольку статический метод находился в томже классе, где и вызывающий его метод Main

           A.StaticProcedure();

           

       }

   }


 

А также другие работы, которые могут Вас заинтересовать

71235. Статистическая сводка и группировка 20.94 KB
  Цель работы: овладеть методикой проведения группировок для определения взаимосвязи изучаемых признаков и изучения состава однородной совокупности. Порядок выполнения работы Осуществить аналитическую группировку статистических данных для определения взаимосвязи...
71236. Статистическое наблюдение 21.22 KB
  Цель исследования: для изучения загруженности школьников. Объект наблюдения: ученики средней школы №2 г. Владимира(9 класс) Единица наблюдения: ученик. Программа наблюдения: Перечень признаков: внимательность ребенка, самостоятельность, активность, усидчивость, утомляемость, общительность со сверстниками (друзьями).
71242. Все работы выполняются с базой данных «Библиотека» 89.78 KB
  Так как нам необходимо вывести только читателя с кодом 1 то в строку условие отбора необходимо ввести: =1 Результат Чтобы запустить запрос на исполнение удостоверьтесь что вы находитесь на ленте инструментов вы находитесь в разделе Конструктор в группе инструментов...