# Функции и процедуры

## Введение в функции и процедуры

**Определение и роль функций и процедур в языке программирования**

**Функции и процедуры** представляют собой фрагменты кода, которые выполняют определенные задачи в программе.

* **Процедура (метод):** Это блок кода, который служит для выполнения последовательности операций, но не возвращает результат. Процедуры применяются для организации кода и выполнения действий, не требующих возврата значений.

  ```csharp
  csharpCopy codevoid PrintHello()
  {
      Console.WriteLine("Hello, world!");
  }
  ```
* **Функция:** Это блок кода, выполняющий определенную задачу и возвращающий результат. Функции используются, когда необходимо получить какое-то значение для дальнейшего использования.

  ```csharp
  csharpCopy codeint Add(int a, int b)
  {
      return a + b;
  }
  ```

**Основные принципы модульного программирования**

* **Разделение на модули:** Функции и процедуры помогают разделить программу на небольшие, логические модули. Каждый модуль отвечает за определенную задачу, что облегчает понимание и изменение кода.
* **Повторное использование кода:** Модульный подход позволяет изолировать функциональность, что способствует повторному использованию кода в различных частях программы.
* **Читаемость кода:** Использование функций и процедур делает код более читаемым и понятным. Каждая функция выполняет конкретную задачу, что улучшает структуру программы.

## Объявление и вызов функций

**Синтаксис объявления функций и процедур**

* **Объявление процедуры:**

  ```csharp
  csharpCopy codevoid PrintMessage(string message)
  {
      Console.WriteLine(message);
  }
  ```

  Здесь `void` указывает на то, что процедура не возвращает значение.
* **Объявление функции:**

  ```csharp
  csharpCopy codeint Add(int a, int b)
  {
      return a + b;
  }
  ```

  Здесь `int` указывает на тип возвращаемого значения.

**Передача параметров в функции**

* **Параметры процедур:**

  ```csharp
  csharpCopy codevoid PrintMessage(string message)
  {
      Console.WriteLine(message);
  }
  ```

  `string message` - это параметр процедуры, который принимает строку в качестве аргумента.
* **Параметры функций:**

  ```csharp
  csharpCopy codeint Add(int a, int b)
  {
      return a + b;
  }
  ```

  `int a` и `int b` - это параметры функции, которые принимают целочисленные значения в качестве аргументов.

**Возвращаемые значения**

* **Процедура без возвращаемого значения:**

  ```csharp
  csharpCopy codevoid PrintMessage(string message)
  {
      Console.WriteLine(message);
  }
  ```

  Здесь `void` означает, что процедура не возвращает значение.
* **Функция с возвращаемым значением:**

  ```csharp
  csharpCopy codeint Add(int a, int b)
  {
      return a + b;
  }
  ```

  Здесь `int` указывает на тип возвращаемого значения (целое число).

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

## Локальные и глобальные переменные

**Различия между локальными и глобальными переменными**

**Локальные переменные:**

* **Область видимости:** Локальные переменные объявляются внутри блока кода (функции, процедуры или блока кода внутри функции).

  ```csharp
  csharpCopy codevoid ExampleFunction()
  {
      int localVar = 10;  // Локальная переменная
      // ...
  }
  ```
* **Доступность:** Локальные переменные видны только внутри блока кода, в котором они объявлены. Их нельзя использовать за пределами этого блока.
* **Жизненный цикл:** Локальные переменные существуют только во время выполнения блока кода, где они были объявлены. После завершения блока кода они уничтожаются.

**Глобальные переменные:**

* **Область видимости:** Глобальные переменные объявляются за пределами функций и имеют доступ ко всему коду в программе.

  ```csharp
  csharpCopy codeint globalVar = 20;  // Глобальная переменная

  void ExampleFunction()
  {
      // globalVar доступна здесь
      // ...
  }
  ```
* **Доступность:** Глобальные переменные видны во всех функциях программы. Однако их использование может сделать код менее читаемым и поддерживаемым.
* **Жизненный цикл:** Глобальные переменные существуют на протяжении всего времени выполнения программы.

**Влияние области видимости на доступ к переменным**

* **Локальные переменные:** Могут быть использованы только внутри того блока кода, где они объявлены. Позволяют избегать конфликтов имен между разными частями программы.
* **Глобальные переменные:** Могут быть использованы в любом месте программы, что может привести к нежелательным эффектам (например, случайному изменению значения из разных частей программы).

## Рекурсия

**Концепция рекурсии**

**Рекурсия** — это процесс, при котором функция вызывает сама себя. Рекурсивные функции решают задачу путем разбиения ее на более простые подзадачи.

**Преимущества и недостатки рекурсивных функций**

**Преимущества:**

* **Читаемость кода:** Рекурсивные решения могут быть более легкими и понятными для понимания.
* **Модульность:** Рекурсия может упростить решение задачи, разбивая ее на более мелкие части.

**Недостатки:**

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

**Примеры рекурсивных алгоритмов**

1. **Факториал:**

   ```csharp
   csharpCopy codeint Factorial(int n)
   {
       if (n == 0 || n == 1)
           return 1;
       else
           return n * Factorial(n - 1);
   }
   ```
2. **Числа Фибоначчи:**

   ```csharp
   csharpCopy codeint Fibonacci(int n)
   {
       if (n <= 1)
           return n;
       else
           return Fibonacci(n - 1) + Fibonacci(n - 2);
   }
   ```

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

## Анонимные функции и замыкания

**Введение в анонимные функции**

**Анонимные функции** в C# представляют собой функции без имени, которые могут быть созданы и использованы во время выполнения программы. Они облегчают создание коротких, локальных функций без необходимости явного объявления.

Пример анонимной функции в виде делегата:

```csharp
csharpCopy codeFunc<int, int, int> add = delegate(int x, int y) { return x + y; };
```

**Использование лямбда-выражений**

**Лямбда-выражения** представляют собой более краткую и выразительную форму для создания анонимных функций. Они особенно полезны при работе с делегатами и интерфейсами с единственным методом (функциональными интерфейсами).

Пример лямбда-выражения для сложения двух чисел:

```csharp
csharpCopy codeFunc<int, int, int> add = (x, y) => x + y;
```

Лямбда-выражение `(x, y) => x + y` эквивалентно анонимной функции `delegate(int x, int y) { return x + y; }`, но более кратко.

**Замыкания и их роль в программировании**

**Замыкания** возникают, когда анонимная функция (или лямбда-выражение) использует переменные из внешней области видимости. В таком случае, переменные сохраняются вместе с функцией, даже если она вызывается вне этой области видимости.

Пример использования замыкания:

```csharp
csharpCopy codeFunc<int, Func<int, int>> createAdder = x =>
{
    return y => x + y;
};

var add5 = createAdder(5);
int result = add5(3);  // Результат: 8
```

В этом примере, замыкание происходит, потому что `add5` сохраняет переменную `x`, которая была определена во внешней функции `createAdder`. Это позволяет `add5` использовать значение `x` при вызове, даже если `createAdder` уже завершил свое выполнение.

Замыкания часто используются для передачи контекста данных или сохранения состояния между вызовами функции. Они предоставляют мощный механизм для работы с функциональными конструкциями в C#.

## Обработка исключений

**Обзор механизма обработки исключений**

**Механизм обработки исключений в C#** предоставляет способ обрабатывать и управлять ошибками, которые могут возникнуть во время выполнения программы. Ошибки, представленные объектами исключений, могут быть перехвачены и обработаны.

**Использование блоков try, catch, finally**

* **Блок `try`:** В этом блоке размещается код, в котором может произойти исключение.

  ```csharp
  csharpCopy codetry
  {
      // Код, где может произойти исключение
  }
  ```
* **Блок `catch`:** В этом блоке указывается, как обработать определенный тип исключения.

  ```csharp
  csharpCopy codecatch (ExceptionType ex)
  {
      // Обработка исключения типа ExceptionType
  }
  ```
* **Блок `finally`:** В этом блоке размещается код, который будет выполнен в любом случае, даже если произошло исключение.

  ```csharp
  csharpCopy codefinally
  {
      // Код, который выполнится всегда
  }
  ```

Пример:

```csharp
csharpCopy codetry
{
    int result = 10 / 0;  // Попытка деления на ноль
}
catch (DivideByZeroException ex)
{
    Console.WriteLine("Ошибка деления на ноль!");
}
finally
{
    Console.WriteLine("Этот код выполнится в любом случае.");
}
```

**Создание собственных исключений**

В C# можно создавать собственные типы исключений для более точной обработки ошибок в приложении.

```csharp
csharpCopy codepublic class CustomException : Exception
{
    public CustomException(string message) : base(message)
    {
    }
}

// Где-то в коде
try
{
    throw new CustomException("Это мое собственное исключение!");
}
catch (CustomException ex)
{
    Console.WriteLine(ex.Message);
}
```

## Функциональное программирование в C\#

**Особенности функционального программирования**

**Функциональное программирование** в C# поддерживается с использованием лямбда-выражений, замыканий и LINQ (Language Integrated Query). Основные принципы:

* **Функции как объекты первого класса:** Функции могут быть переданы как аргументы, возвращены из других функций и присвоены переменным.
* **Замыкания:** Возможность захвата и использования переменных из окружающего контекста внутри функции.
* **Избегание изменяемых состояний:** Предпочтение использования неизменяемых структур данных и функций без побочных эффектов.

**Применение лямбда-выражений и LINQ**

**Лямбда-выражения** предоставляют краткий синтаксис для создания анонимных функций. Пример:

```csharp
csharpCopy codeFunc<int, int, int> add = (x, y) => x + y;
```

**LINQ (Language Integrated Query)** позволяет писать выражения запросов для обработки данных, в том числе коллекций.

```csharp
csharpCopy codevar evenNumbers = numbers.Where(n => n % 2 == 0);
```

**Избегание изменяемых состояний**

В функциональном программировании стараются избегать изменяемых состояний (mutable state) и побочных эффектов. Вместо этого используются неизменяемые структуры данных и функции, которые возвращают новые объекты, не изменяя существующие.

```csharp
csharpCopy code// Плохо (изменяемое состояние)
List<int> numbers = new List<int> { 1, 2, 3 };
numbers.Add(4);

// Хорошо (неизменяемое состояние)
List<int> numbers = new List<int> { 1, 2, 3 };
List<int> newNumbers = numbers.Concat(new List<int> { 4 }).ToList();
```

Функциональное программирование в C# поддерживает принципы функционального стиля, хотя C# остается мультипарадигменным языком программирования. Это позволяет писать более выразительный и поддерживаемый код.

## Практические примеры использования функций и процедур

**Разработка функций для решения конкретных задач**

**Пример 1: Вычисление факториала**

```csharp
csharpCopy codepublic static int Factorial(int n)
{
    if (n == 0 || n == 1)
        return 1;
    else
        return n * Factorial(n - 1);
}
```

**Пример 2: Поиск максимального элемента в массиве**

```csharp
csharpCopy codepublic static int FindMax(int[] array)
{
    if (array == null || array.Length == 0)
        throw new ArgumentException("Массив не должен быть пустым.");

    int max = array[0];
    foreach (var number in array)
    {
        if (number > max)
            max = number;
    }

    return max;
}
```

**Примеры процедурной обработки данных**

**Пример: Сортировка массива методом пузырька**

```csharp
csharpCopy codepublic static void BubbleSort(int[] array)
{
    if (array == null || array.Length <= 1)
        return;

    for (int i = 0; i < array.Length - 1; i++)
    {
        for (int j = 0; j < array.Length - i - 1; j++)
        {
            if (array[j] > array[j + 1])
            {
                int temp = array[j];
                array[j] = array[j + 1];
                array[j + 1] = temp;
            }
        }
    }
}
```

## Лучшие практики при работе с функциями и процедурами

**Правила именования функций и переменных**

* Имена должны быть описательными и понятными.
* Используйте camelCase (первое слово с маленькой буквы, последующие с заглавной) для переменных и названий функций.
* Избегайте слишком коротких имен, которые могут быть непонятны.

**Избегание длинных цепочек вызовов**

* Соблюдайте принцип единственной ответственности: функции и процедуры должны выполнять конкретные задачи.
* Длинные цепочки вызовов усложняют отладку и поддержку кода.

**Проектирование функций с учетом повторного использования**

* Функции и процедуры должны быть разработаны так, чтобы их можно было легко повторно использовать в других частях программы.
* Избегайте написания "жестко связанных" функций, которые трудно переиспользовать.

## Тестирование функций и процедур

**Введение в тестирование программного обеспечения**

* **Тестирование** — это процесс проверки программы с целью выявления ошибок.
* **Тестирование функций и процедур** включает в себя создание тестовых случаев для проверки их правильной работы.

**Методы тестирования функций и процедур**

* **Модульное тестирование:** Проверка отдельных модулей (функций, процедур) на корректность работы.
* **Интеграционное тестирование:** Проверка взаимодействия между различными модулями программы.
* **Приемочное тестирование:** Проверка программы на соответствие заявленным требованиям.

**Использование тестовых фреймворков**

* **NUnit, xUnit, MSTest** и другие тестовые фреймворки предоставляют инструменты для автоматизации тестирования.
* Тестовые фреймворки позволяют создавать и запускать тестовые сценарии, а также анализировать результаты.

## Интеграция функций и процедур в приложения

**Проектирование модульной архитектуры приложения**

* **Модульность** — это принцип проектирования, при котором приложение разбивается на независимые модули.
* Каждый модуль должен выполнять конкретную функцию и быть самодостаточным.

**Использование функций и процедур в больших проектах**

* **Разделение на слои:** Разделяйте функции и процедуры на слои (например, слой данных, слой бизнес-логики, слой пользовательского интерфейса).
* **Интерфейсы:** Используйте четкие интерфейсы для взаимодействия между различными частями приложения.

**Работа с библиотеками и сторонними модулями**

* **Использование библиотек:** Встраивайте в проект проверенные библиотеки для выполнения общих задач.
* **Стандартизация:** Соблюдайте стандарты кодирования и использования библиотек для обеспечения единообразия в проекте.

Проектирование приложения с учетом лучших практик функционального программирования, тестирования и модульности облегчит его поддержку и развитие.

***

## Упражнения

#### **Задача 1: Расчет факториала**

Напишите функцию `CalculateFactorial`, которая принимает целое положительное число в качестве аргумента и возвращает его факториал. Вызовите функцию для расчета факториала числа 5.

<details>

<summary>Решение</summary>

```csharp
// Задача 1: Расчет факториала
using System;

class Program
{
    static void Main()
    {
        int number = 5;
        long factorial = CalculateFactorial(number);
        Console.WriteLine($"Факториал числа {number}: {factorial}");
    }

    static long CalculateFactorial(int n)
    {
        if (n == 0 || n == 1)
        {
            return 1;
        }
        else
        {
            return n * CalculateFactorial(n - 1);
        }
    }
}
```

</details>

#### **Задача 2: Проверка на четность**

Напишите функцию `IsEven`, которая принимает целое число в качестве аргумента и возвращает `true`, если число четное, и `false`, если нечетное. Проверьте функцию для чисел 7 и 10.

<details>

<summary>Решение</summary>

```csharp
// Задача 2: Проверка на четность
using System;

class Program
{
    static void Main()
    {
        int number1 = 7;
        int number2 = 10;

        Console.WriteLine($"Число {number1} четное? {IsEven(number1)}");
        Console.WriteLine($"Число {number2} четное? {IsEven(number2)}");
    }

    static bool IsEven(int number)
    {
        return number % 2 == 0;
    }
}
```

</details>

#### **Задача 3: Сложение двух чисел**

Напишите функцию `AddNumbers`, которая принимает два параметра типа int и возвращает их сумму. Проверьте функцию, передав ей числа 8 и 12.

<details>

<summary>Решение</summary>

```csharp
// Задача 3: Сложение двух чисел
using System;

class Program
{
    static void Main()
    {
        int a = 8;
        int b = 12;

        int sum = AddNumbers(a, b);
        Console.WriteLine($"Сумма чисел {a} и {b}: {sum}");
    }

    static int AddNumbers(int x, int y)
    {
        return x + y;
    }
}
```

</details>

#### **Задача 4: Генерация последовательности чисел**

Напишите функцию `GenerateSequence`, которая принимает начальное значение, конечное значение и шаг, а затем возвращает последовательность чисел в указанном диапазоне с указанным шагом. Проверьте функцию для диапазона от 1 до 10 с шагом 2.

<details>

<summary>Решение</summary>

```csharp
// Задача 4: Генерация последовательности чисел
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        int start = 1;
        int end = 10;
        int step = 2;

        List<int> sequence = GenerateSequence(start, end, step);
        Console.WriteLine($"Последовательность: {string.Join(", ", sequence)}");
    }

    static List<int> GenerateSequence(int start, int end, int step)
    {
        List<int> sequence = new List<int>();

        for (int i = start; i <= end; i += step)
        {
            sequence.Add(i);
        }

        return sequence;
    }
}
```

</details>

#### **Задача 5: Поиск максимального значения**

Напишите функцию `FindMax`, которая принимает массив чисел и возвращает максимальное значение в массиве. Проверьте функцию на массиве \[5, 12, 8, 3, 9].

<details>

<summary>Решение</summary>

```csharp
// Задача 5: Поиск максимального значения
using System;
using System.Linq;

class Program
{
    static void Main()
    {
        int[] numbers = { 5, 12, 8, 3, 9 };
        int max = FindMax(numbers);
        Console.WriteLine($"Максимальное значение в массиве: {max}");
    }

    static int FindMax(int[] array)
    {
        return array.Max();
    }
}
```

</details>

#### **Задача 6: Возведение в степень**

Напишите функцию `Power`, которая принимает два числа (основание и показатель степени) и возвращает результат возведения в степень. Проверьте функцию для основания 2 и степени 3.

<details>

<summary>Решение</summary>

```csharp
// Задача 6: Возведение в степень
using System;

class Program
{
    static void Main()
    {
        int baseNumber = 2;
        int exponent = 3;

        double result = Power(baseNumber, exponent);
        Console.WriteLine($"{baseNumber} в степени {exponent}: {result}");
    }

    static double Power(int x, int y)
    {
        return Math.Pow(x, y);
    }
}
```

</details>

***

## Вопросы

#### **Что такое функция в программировании?**

Определите понятие функции в контексте программирования. Какие основные элементы включает в себя функция?

#### **Чем отличаются процедуры от функций в языке C#?**

Какие основные различия между процедурами и функциями в языке программирования C#? Приведите примеры каждого из них.

#### **Что такое область видимости переменных в функциях?**

Объясните понятие области видимости переменных в функциях. Как область видимости влияет на доступность переменных в программе?

#### **Как передаются параметры в функции C#?**

Изложите различные способы передачи параметров в функции языка программирования C#. Какие существуют виды передачи параметров?

#### **Что такое рекурсия и как она используется в функциональном программировании?**

Определите понятие рекурсии. Как функции могут вызывать сами себя, и в каких случаях это полезно?

#### **Что такое возвращаемое значение функции?**

Как функции возвращают значения в языке C#? Что представляет собой возвращаемое значение, и как его можно использовать?

#### **Какие бывают типы функций в C# с точки зрения возвращаемого значения?**

Какие различные типы функций существуют в C# в зависимости от возвращаемого значения? Приведите примеры.

#### **Что такое локальные и глобальные переменные в функциях?**

Определите понятия локальных и глобальных переменных в функциональном программировании. Как они отличаются друг от друга?

#### **Что такое перегрузка функций?**

Каким образом в языке C# реализуется перегрузка функций? Какие условия должны быть соблюдены для успешной перегрузки?

#### **Какие преимущества и недостатки связаны с использованием функций в программировании?**

Обсудите преимущества и недостатки использования функций в программах. Как функции способствуют повторному использованию кода и поддерживаемости программы?

***

## Тесты

{% embed url="<https://docs.google.com/forms/d/e/1FAIpQLSeboLeHHcl-I0egULf1KtK6EG7_q8Tj4CmVij8lFbNt21ds7w/viewform?usp=sf_link>" fullWidth="true" %}
